Merge with develop

This commit is contained in:
Steven Miers 2015-08-15 17:18:15 -05:00
commit 6edb4b6012
59 changed files with 4573 additions and 1376 deletions

View File

@ -5,9 +5,14 @@ ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do
config.sort_order = 'name_asc'
config.batch_actions = false
filter :genre
filter :genres
filter :status, :as => :select, collection: JamRuby::JamTrack::STATUS
scope("Default", default: true) { |scope| scope }
scope("Onboarding TODO") { |scope| scope.where('onboarding_exceptions is not null') }
scope("Tency Only") { |scope| scope.joins('INNER JOIN jam_track_licensors as licensors ON jam_tracks.licensor_id = licensors.id').where("licensors.name = 'Tency Music'") }
scope("Onboarding TODO w/ Tency Only") { |scope| scope.joins('INNER JOIN jam_track_licensors as licensors ON jam_tracks.licensor_id = licensors.id').where("licensors.name = 'Tency Music'").where('onboarding_exceptions is not null') }
form :partial => 'form'
index do
@ -24,11 +29,21 @@ ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do
column :original_artist
column :name
column :onboarding_flags do |jam_track| jam_track.onboard_warnings end
column :onboarding_exceptions do |jam_track|
if jam_track.onboarding_exceptions
exceptions = JSON.parse(jam_track.onboarding_exceptions)
exceptions.keys.join(',')
else
''
end
end
column :status
column :master_track do |jam_track| jam_track.master_track.nil? ? 'None' : (link_to "Download", jam_track.master_track.url_by_sample_rate(44)) end
column :licensor
column :genre
column :genres do |jam_track|
jam_track.genres.map(&:description).join(',')
end
column :price
column :reproduction_royalty
column :public_performance_royalty

View File

@ -12,7 +12,7 @@
= f.input :songwriter, :input_html => { :rows=>1, :maxlength=>1000 }
= f.input :publisher, :input_html => { :rows=>1, :maxlength=>1000 }
= f.input :licensor, collection: JamRuby::JamTrackLicensor.all, include_blank: true
= f.input :genre, collection: JamRuby::Genre.all, include_blank: false
= f.input :genres
= f.input :duration, hint: 'this should rarely need editing because it comes from the import process'
= f.input :sales_region, collection: JamRuby::JamTrack::SALES_REGION, include_blank: false
= f.input :price, :required => true, :input_html => {type: 'numeric'}

View File

@ -16,7 +16,6 @@ class JamRuby::JamTrack
end
def jmep_json_generate
self.genre_id = nil if self.genre_id == ''
self.licensor_id = nil if self.licensor_id == ''
self.jmep_json = nil if self.jmep_json == ''
self.time_signature = nil if self.time_signature == ''

View File

@ -299,3 +299,5 @@ enhance_band_profile.sql
alter_band_profile_rate_defaults.sql
repair_band_profile.sql
profile_teacher.sql
jam_track_onboarding_enhancements.sql
jam_track_name_drop_unique.sql

View File

@ -0,0 +1 @@
ALTER TABLE jam_tracks DROP CONSTRAINT jam_tracks_name_key;

View File

@ -0,0 +1,78 @@
-- "rsvp_slots_instrument_id_fkey" FOREIGN KEY (instrument_id) REFERENCES instruments(id)
-- "musicians_instruments_instrument_id_fkey" FOREIGN KEY (instrument_id) REFERENCES instruments(id) ON DELETE CASCADE
-- "saved_tracks_instrument_id_fkey" FOREIGN KEY (instrument_id) REFERENCES instruments(id) ON DELETE CASCADE
ALTER TABLE rsvp_slots DROP CONSTRAINT rsvp_slots_instrument_id_fkey;
ALTER TABLE musicians_instruments DROP CONSTRAINT musicians_instruments_instrument_id_fkey;
ALTER TABLE recorded_tracks DROP CONSTRAINT saved_tracks_instrument_id_fkey;
UPDATE instruments SET id = 'double bass', description = 'Double Bass' WHERE id = 'upright bass';
UPDATE rsvp_slots SET instrument_id = 'double bass' where instrument_id = 'upright bass';
UPDATE musicians_instruments SET instrument_id = 'double bass' where instrument_id = 'upright bass';
UPDATE recorded_tracks SET instrument_id = 'double bass' where instrument_id = 'upright bass';
ALTER TABLE rsvp_slots ADD CONSTRAINT rsvp_slots_instrument_id_fkey FOREIGN KEY (instrument_id) REFERENCES instruments(id) ON DELETE SET NULL;
ALTER TABLE musicians_instruments ADD CONSTRAINT musicians_instruments_instrument_id_fkey FOREIGN KEY (instrument_id) REFERENCES instruments(id) ON DELETE CASCADE;
ALTER TABLE recorded_tracks ADD CONSTRAINT saved_tracks_instrument_id_fkey FOREIGN KEY (instrument_id) REFERENCES instruments(id) ON DELETE CASCADE;
INSERT INTO instruments (id, description, popularity) VALUES ('steel guitar', 'Steel Guitar', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('orchestra', 'Orchestra', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('glockenspiel', 'Glockenspiel', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('dobro', 'Dobro', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('harp', 'Harp', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('vocoder', 'Vocoder', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('flugelhorn', 'Flugelhorn', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('timpani', 'Timpani', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('bassoon', 'Bassoon', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('charango', 'Charango', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('theremin', 'Theremin', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('sitar', 'Sitar', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('piccolo', 'Piccolo', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('bagpipes', 'Bagpipes', 1);
ALTER TABLE jam_tracks ADD COLUMN onboarding_exceptions JSON;
ALTER TABLE jam_track_tracks ADD COLUMN original_filename VARCHAR;
ALTER TABLE jam_tracks ADD COLUMN additional_info VARCHAR;
ALTER TABLE jam_tracks ADD COLUMN language VARCHAR NOT NULL DEFAULT 'eng';
ALTER TABLE jam_tracks ADD COLUMN year INTEGER;
ALTER TABLE jam_tracks ADD COLUMN vendor_id VARCHAR;
INSERT INTO jam_track_licensors (name, description) VALUES ('Tency Music', 'Tency Music is a music production company specialized in re-recordings.');
CREATE TABLE genres_jam_tracks (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
jam_track_id VARCHAR(64) NOT NULL REFERENCES jam_tracks(id) ON DELETE CASCADE,
genre_id VARCHAR(64) NOT NULL REFERENCES genres(id) ON DELETE CASCADE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO genres_jam_tracks (jam_track_id, genre_id) ((SELECT jam_tracks.id, jam_tracks.genre_id FROM jam_tracks));
ALTER TABLE jam_tracks DROP COLUMN genre_id;
-- holds precount, click.wav, click.txt
CREATE TABLE jam_track_files (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
jam_track_id VARCHAR(64) REFERENCES jam_tracks(id) ON DELETE CASCADE,
file_type VARCHAR NOT NULL,
original_filename VARCHAR NOT NULL,
precount_num INTEGER,
url VARCHAR,
md5 VARCHAR,
length bigint,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO genres (id, description) VALUES ('soft rock', 'Soft Rock');
INSERT INTO genres (id, description) VALUES ('rap', 'Rap');
INSERT INTO genres (id, description) VALUES ('tv & movie soundtrack', 'TV & Movie Soundtrack');
INSERT INTO genres (id, description) VALUES ('holiday', 'Holiday');
INSERT INTO genres (id, description) VALUES ('kids', 'Kids');
INSERT INTO genres (id, description) VALUES ('disco', 'Disco');
INSERT INTO genres (id, description) VALUES ('soul', 'Soul');
INSERT INTO genres (id, description) VALUES ('hard rock', 'Hard Rock');
INSERT INTO genres (id, description) VALUES ('funk', 'Funk');
INSERT INTO genres (id, description) VALUES ('dance', 'Dance');
INSERT INTO genres (id, description) VALUES ('creole', 'Creole');
INSERT INTO genres (id, description) VALUES ('traditional', 'Traditional');
INSERT INTO genres (id, description) VALUES ('oldies', 'Oldies');
INSERT INTO genres (id, description) VALUES ('world', 'World');
INSERT INTO genres (id, description) VALUES ('musical', 'Musical');
INSERT INTO genres (id, description) VALUES ('celtic', 'Celtic');

View File

@ -206,6 +206,8 @@ require "jam_ruby/models/jam_track"
require "jam_ruby/models/jam_track_track"
require "jam_ruby/models/jam_track_right"
require "jam_ruby/models/jam_track_tap_in"
require "jam_ruby/models/jam_track_file"
require "jam_ruby/models/genre_jam_track"
require "jam_ruby/app/mailers/async_mailer"
require "jam_ruby/app/mailers/batch_mailer"
require "jam_ruby/app/mailers/progress_mailer"
@ -237,11 +239,14 @@ require "jam_ruby/jmep_manager"
require "jam_ruby/models/performance_sample"
require "jam_ruby/models/online_presence"
require "jam_ruby/models/json_store"
require "jam_ruby/models/base_search"
require "jam_ruby/models/musician_search"
require "jam_ruby/models/teacher"
require "jam_ruby/models/teacher_experience"
require "jam_ruby/models/language"
require "jam_ruby/models/subject"
require "jam_ruby/models/band_search"
require "jam_ruby/import/tency_stem_mapping"
include Jampb

View File

@ -0,0 +1,360 @@
module JamRuby
# this is probably a one-off class used to map Tency-named stems into JamKazam-named stems
class TencyStemMapping
@@log = Logging.logger[TencyStemMapping]
def s3_manager
@s3_manager ||= S3Manager.new('jamkazam-tency', APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
end
def initialize
@originals_folder = "/Volumes/sethcall/Dropbox/seth@jamkazam.com/JamTracks - Tency Music - Original Folder for Normalization Map"
@mapping_folder = "/Volumes/sethcall/Dropbox/seth@jamkazam.com/JamTracks - Tency Music"
@original_songs = {}
@mapping_songs = {}
@mappings = {}
end
def create_map
tency_originals
tency_maps
dump
end
def create_mapping_map
tency_maps
dump_map
end
def hydrate
@original_songs = YAML.load_file('original_songs.yml')
@mapping_songs = YAML.load_file('mapping_songs.yml')
end
def parse_sanitized_filename(filename)
instrument = nil
part = nil
basename = File.basename(filename)
stem = basename.index('Stem')
if stem
stripped = basename[(stem + 'Stem'.length)..-5] # takes of 'stem' and '.wav'
stripped.strip!
dash = stripped.index('-')
if dash == 0
stripped = stripped[1..-1].strip!
# now we should have something like "Vocal - Lead" (instrument - part)
instrument, part = stripped.split('-')
instrument.strip! if instrument
part.strip! if part
else
"no or misplaced dash for #{filename}"
end
else
raise "no stem for #{filename}"
end
[instrument, part]
end
# For all the tracks that I have labeled manually as
# Instrument = Upright Bass and Part = Upright Bass,
# can you please change both the Instrument and Part to Double Bass instead?
#
def check_mappings
missing_instrument = 0
missing_part = 0
part_names = []
hydrate
@mapping_songs.each do |cache_id, data|
mapped_filename = data[:filename]
@@log.debug("parsing #{mapped_filename}")
instrument, part = parse_sanitized_filename(mapped_filename)
@@log.debug("parsed #{instrument} (#{part})")
missing_instrument = missing_instrument + 1 unless instrument
missing_part = missing_part + 1 unless part
part_names << mapped_filename unless part
end
@@log.info("SUMMARY")
@@log.info("-------")
@@log.info("missing instruments:#{missing_instrument} missing parts: #{missing_part}")
@@log.info("files with no parts: #{part_names}")
# files with no parts:
# ["Huey Lewis And The News - Heart And Soul - 31957/Heart And Soul Stem - Synth 2.wav",
# "ZZ Top - Tush - 20852/Tush Stem - Clicktrack.wav",
# "Crosby Stills And Nash - Teach Your Children - 15440/Teach Your Children Stem - Bass Guitar.wav",
# /Brad Paisley - She's Everything - 19886/She's Everything Stem - Clicktrack.wav",
# "Toby Keith - Beer For My Horses - 7221/Beer For My Horses Stem - Lap Steel.wav",
# Toby Keith - Beer For My Horses - 7221/Beer For My Horses Stem - Acoustic Guitar.wav"
end
def track_mapping(basename, instr_part)
instrument = instr_part[:instrument]
part = instr_part[:part]
basename.downcase!
info = @mappings[basename]
unless info
info = {matches:[]}
@mappings[basename] = info
end
info[:matches] << instr_part
end
def correlate
mapped = 0
unmapped = 0
unmapped_details = []
no_instrument = []
common_unknown_instruments = {}
hydrate
@mapping_songs.each do |cache_id, data|
# go through each track hand-mapped, and find it's matching song if any.
mapped_filename = data[:filename]
found_original = @original_songs[cache_id]
if found_original
# mapping made
original_filename = found_original[:filename]
original_basename = File.basename(original_filename).downcase
mapped = mapped + 1
instrument, part = parse_sanitized_filename(mapped_filename)
instr_part = JamTrackImporter.determine_instrument(instrument, part)
instr_part[:instrument]
if instr_part[:instrument]
# track the mapping of this one
track_mapping(original_basename, instr_part)
else
@@log.error("unable to determine instrument for #{File.basename(mapped_filename)}")
no_instrument << ({filename: File.basename(mapped_filename), instrument: instrument, part: part})
common_unknown_instruments["#{instrument}-(#{part})"] = 1
end
else
unmapped = unmapped + 1
unmapped_details << {filename: mapped_filename}
end
end
puts("SUMMARY")
puts("-------")
puts("MAPPED:#{mapped} UNMAPPED:#{unmapped}")
unmapped_details.each do |unmapped_detail|
puts "UNMAPPED FILE: #{File.basename(unmapped_detail[:filename])}"
end
puts("UNKNOWN INSTRUMENT: #{no_instrument.length}")
no_instrument.each do |item|
puts("UNKNOWN INSTRUMENT: #{item[:filename]}")
end
common_unknown_instruments.each do |key, value|
puts("#{key}")
end
@mappings.each do |basename, mapping|
matches = mapping[:matches]
counts = matches.each_with_object(Hash.new(0)) { |word,counts| counts[word] += 1 }
ordered_matches = counts.sort_by {|k, v| -v}
output = ""
ordered_matches.each do |match|
detail = match[0]
count = match[1]
output << "#{detail[:instrument]}(#{detail[:part]})/#{count}, "
end
puts "map detail: #{basename}: #{output}"
mapping[:ordered] = ordered_matches
mapping[:detail] = output
end
CSV.open("mapping.csv", "wb") do |csv|
@mappings.each do |basename, mapping|
item = mapping[:ordered]
trust_worthy = item.length == 1
unless trust_worthy
# if the 1st item is at least 4 'counts' more than the next item, we can consider it trust_worthy
if item[0][1] - 4 > item[1][1]
trust_worthy = true
end
end
csv << [ basename, item[0][0][:instrument], item[0][0][:part], item[0][1], trust_worthy ]
end
end
CSV.open("determinate-single-matches.csv", "wb") do |csv|
@mappings.each do |basename, mapping|
if mapping[:ordered].length == 1 && mapping[:ordered][0][1] == 1
item = mapping[:ordered]
csv << [ basename, item[0][0][:instrument], item[0][0][:part], item[0][1] ]
end
end
end
CSV.open("determinate-multi-matches.csv", "wb") do |csv|
@mappings.each do |basename, mapping|
if mapping[:ordered].length == 1 && mapping[:ordered][0][1] > 1
item = mapping[:ordered]
csv << [ basename, item[0][0][:instrument], item[0][0][:part], item[0][1] ]
end
end
end
CSV.open("ambiguous-matches.csv", "wb") do |csv|
@mappings.each do |basename, mapping|
if mapping[:ordered].length > 1
csv << [ basename, mapping[:detail] ]
end
end
end
end
def dump
File.open('original_songs.yml', 'w') {|f| f.write(YAML.dump(@original_songs)) }
File.open('mapping_songs.yml', 'w') {|f| f.write(YAML.dump(@mapping_songs)) }
end
def dump_map
File.open('mapping_songs.yml', 'w') {|f| f.write(YAML.dump(@mapping_songs)) }
end
def md5(filepath)
Digest::MD5.file(filepath).hexdigest
end
def tency_original_check
songs = Pathname.new(@originals_folder).children.select { |c| c.directory? }
songs.each do |song|
dirs = Pathname.new(song).children.select {|c| c.directory? }
@@log.debug "SONG #{song}"
dirs.each do |dir|
@@log.debug "#{dir.basename.to_s}"
end
@@log.debug ""
end
end
def tency_originals
songs = Pathname.new(@originals_folder).children.select { |c| c.directory? }
songs.each do |filename|
id = parse_id(filename.basename.to_s )
files = Pathname.new(filename).children.select {|c| c.file? }
# also look into any 1st level folders we might find
dirs = Pathname.new(filename).children.select {|c| c.directory? }
dirs.each do |dir|
more_tracks = Pathname.new(dir).children.select {|c| c.file? }
files = files + more_tracks
end
files.each do |file|
@@log.debug("processing original track #{file.to_s}")
md5 = md5(file.to_s)
song = {md5:md5, filename:file.to_s, id:id}
@original_songs[cache_id(id, md5)] = song
end
end
end
def tency_maps
songs = Pathname.new(@mapping_folder).children.select { |c| c.directory? }
songs.each do |song_filename|
id = parse_id_mapped(song_filename.basename.to_s )
@@log.debug "processing song #{song_filename.to_s}"
tracks = Pathname.new(song_filename).children.select {|c| c.file? }
tracks.each do |track|
if track.to_s.include? "Stem"
@@log.debug("processing mapped track #{track.to_s}")
md5 = md5(track.to_s)
song = {md5:md5, filename:track.to_s}
@mapping_songs[cache_id(id, md5)] = song
end
end
end
end
def cache_id(id, md5)
"#{id}-#{md5}"
end
def parse_id(filename)
#amy-winehouse_you-know-i-m-no-good-feat-ghostface-killah_11767
index = filename.rindex('_')
if index
id = filename[(index + 1)..-1]
if id.end_with?('/')
id = id[0...-1]
end
id = id.to_i
if id == 0
raise "no valid ID in filename: #{filename}"
end
else
raise "no _ in filename: #{filename}"
end
id
end
def parse_id_mapped(filename)
#Flyleaf - I'm So Sick - 15771
index = filename.rindex('-')
if index
id = filename[(index + 1)..-1]
if id.end_with?('/')
id = id[0...-1]
end
id.strip!
id = id.to_i
if id == 0
raise "no valid ID in filename: #{filename}"
end
else
raise "no - in filename: #{filename}"
end
id
end
def tency_originals2
s3_manager.list_directories('mapper').each do |song_folder|
@@log.debug("searching through tency directory. song folder:'#{song_folder}'")
id = parse_id(song_folder)
@@log.debug("ID #{id}")
top_folder = s3_manager.list_directories(song_folder)
end
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -71,6 +71,7 @@ module JamRuby
end
def follower_count
# FIXME: this could be a lot of followers; calling size loads all the data into memory
self.followers.size
end

View File

@ -0,0 +1,428 @@
module JamRuby
class BandSearch < BaseSearch
cattr_accessor :jschema, :search_meta
attr_accessor :user_counters
serialize :data_blob, JSON
KEY_BAND_SEARCH_TYPE = 'band_search_type'
KEY_BAND_TYPE = 'band_type'
KEY_BAND_STATUS = 'band_status'
KEY_PLAY_COMMIT = 'play_commitment'
KEY_TOUR_OPTION = 'touring_option'
KEY_PERF_SAMPLES = 'performance_samples'
KEY_HIRE_MAX_COST = 'max_cost'
KEY_HIRE_FREE = 'free_gigs'
TO_JOIN = 'to_join'
TO_HIRE = 'to_hire'
BAND_SEARCH_TYPE_VALS = [TO_JOIN, TO_HIRE]
BAND_SEARCH_TYPES = {
TO_JOIN => 'search bands',
TO_HIRE => 'search bands to hire',
}
BAND_TYPE_VAL_STRS = [ANY_VAL_STR, 'amateur', 'professional']
BAND_TYPES = {
BAND_TYPE_VAL_STRS[0] => BAND_TYPE_VAL_STRS[0].camelcase,
BAND_TYPE_VAL_STRS[1] => BAND_TYPE_VAL_STRS[1].camelcase,
BAND_TYPE_VAL_STRS[2] => BAND_TYPE_VAL_STRS[2].camelcase,
}
SORT_VALS = %W{ distance }
SORT_ORDERS = {
SORT_VALS[0] => 'Distance to Me'
}
# SORT_VALS = %W{ distance latency }
# SORT_ORDERS = {
# SORT_VALS[0] => 'Distance to Me'
# SORT_VALS[1] => 'Latency to Me',
# }
HIRE_SORT_VALS = %W{ distance price_asc price_desc }
HIRE_SORT_ORDERS = {
HIRE_SORT_VALS[0] => 'Distance to Me',
HIRE_SORT_VALS[1] => 'Gig Minimum Price (Low to High)',
HIRE_SORT_VALS[2] => 'Gig Minimum Price (High to Low)',
}
BAND_STATUS_VALS = [ANY_VAL_STR,
GenrePlayer::VIRTUAL_BAND,
GenrePlayer::TRADITIONAL_BAND,
]
BAND_STATUS = {
BAND_STATUS_VALS[0] => 'Any',
BAND_STATUS_VALS[1] => 'Virtual Band',
BAND_STATUS_VALS[2] => 'Traditional Band',
}
PLAY_COMMIT_VALS = [ANY_VAL_STR,
'1',
'2',
'3',
'4',
]
PLAY_COMMITS = {
PLAY_COMMIT_VALS[0] => 'Any',
PLAY_COMMIT_VALS[1] => 'Infrequent',
PLAY_COMMIT_VALS[2] => 'Once a Week',
PLAY_COMMIT_VALS[3] => '2-3 Times Per Week',
PLAY_COMMIT_VALS[4] => '4+ Times Per Week',
}
TOUR_OPTION_VALS = [ANY_VAL_STR,
VAL_YES,
VAL_NO,
]
TOUR_OPTIONS = {
TOUR_OPTION_VALS[0] => 'Any',
TOUR_OPTION_VALS[1] => VAL_YES,
TOUR_OPTION_VALS[2] => VAL_NO,
}
PERF_SAMPLES_VALS = TOUR_OPTION_VALS.clone
PERF_SAMPLES = TOUR_OPTIONS.clone
COUNT_FOLLOW = :count_follow
COUNT_RECORD = :count_record
COUNT_SESSION = :count_session
def self.json_schema
return @@jschema if @@jschema
@@jschema = {
TO_JOIN => BaseSearch.json_schema.merge({
KEY_SORT_ORDER => self::SORT_VALS[0],
KEY_BAND_TYPE => self::BAND_TYPE_VAL_STRS[0].to_s,
KEY_BAND_STATUS => BAND_STATUS_VALS[0],
KEY_PLAY_COMMIT => PLAY_COMMIT_VALS[0],
KEY_TOUR_OPTION => TOUR_OPTION_VALS[0],
}),
TO_HIRE => {
KEY_SORT_ORDER => self::HIRE_SORT_VALS[0],
KEY_GENRES => [],
KEY_GIGS => self::GIG_COUNTS[0].to_s,
KEY_BAND_STATUS => BAND_STATUS_VALS[0],
KEY_PERF_SAMPLES => self::PERF_SAMPLES_VALS[0],
KEY_HIRE_MAX_COST => 0,
KEY_HIRE_FREE => 0,
},
}
end
def self.search_filter_meta
return @@search_meta if @@search_meta
toJoinMeta = super(self.json_schema[TO_JOIN])
toJoinMeta.merge!({
KEY_BAND_TYPE => { keys: BAND_TYPE_VAL_STRS, map: BAND_TYPES },
KEY_BAND_STATUS => { keys: BAND_STATUS_VALS, map: BAND_STATUS },
KEY_PLAY_COMMIT => { keys: PLAY_COMMIT_VALS, map: PLAY_COMMITS },
KEY_TOUR_OPTION => { keys: TOUR_OPTION_VALS, map: TOUR_OPTIONS }
})
toHireMeta = super(self.json_schema[TO_HIRE],
{ keys: HIRE_SORT_VALS, map: HIRE_SORT_ORDERS })
toHireMeta.merge!({
KEY_BAND_STATUS => { keys: BAND_STATUS_VALS, map: BAND_STATUS },
KEY_PERF_SAMPLES => { keys: PERF_SAMPLES_VALS, map: PERF_SAMPLES },
})
@@search_meta = {
TO_JOIN => toJoinMeta,
TO_HIRE => toHireMeta,
}
end
def self.search_target_class
Band
end
def _genres(rel, filter)
super(rel, filter)
end
def _concert_gigs(rel, filter)
gg = filter[KEY_GIGS].to_i
rel = rel.where(concert_count: gg) if 0 <= gg
rel
end
def _band_status(rel, filter)
case filter[KEY_BAND_STATUS]
when GenrePlayer::VIRTUAL_BAND
rel.where(band_status: GenrePlayer::VIRTUAL_BAND.sub('_band',''))
when GenrePlayer::TRADITIONAL_BAND
rel.where(band_status: GenrePlayer::TRADITIONAL_BAND.sub('_band',''))
else
rel
end
end
def _play_commitment(rel, filter)
unless ANY_VAL_STR == filter[KEY_PLAY_COMMIT]
rel = rel.where(play_commitment: filter[KEY_PLAY_COMMIT].to_i)
end
rel
end
def _touring_option(rel, filter)
case filter[KEY_TOUR_OPTION]
when VAL_YES
rel.where(touring_option: true)
when VAL_NO
rel.where(touring_option: false)
else
rel
end
end
def _performance_samples(rel, filter)
case filter[KEY_PERF_SAMPLES]
when VAL_YES
rel.joins("LEFT OUTER JOIN performance_samples AS ps ON ps.player_id = bands.id AND player_type = '#{Band.name}'").where(["ps.id IS NOT NULL"])
when VAL_NO
rel.joins("LEFT OUTER JOIN performance_samples AS ps ON ps.player_id = bands.id AND player_type = '#{Band.name}'").where(["ps.id IS NULL"])
else
rel
end
end
def _max_cost(rel, filter)
if 0 < (max_cost = filter[KEY_HIRE_MAX_COST].to_i)
col = Band.arel_table[:gig_minimum]
rel = rel.where(col.lteq(max_cost)).where(col.gt(0))
end
rel
end
def _free_gigs(rel, filter)
case filter[KEY_HIRE_FREE]
when VAL_YES
rel.where(free_gigs: true)
when VAL_NO
rel.where(free_gigs: false)
else
rel
end
end
def _band_type(rel, filter)
case filter[KEY_BAND_TYPE]
when BAND_TYPE_VAL_STRS[1]
rel.where(band_type: BAND_TYPE_VAL_STRS[1])
when BAND_TYPE_VAL_STRS[2]
rel.where(band_type: BAND_TYPE_VAL_STRS[2])
else
rel
end
end
def _sort_order(rel, filter)
val = filter[KEY_SORT_ORDER]
if 'distance' == val || val.blank?
locidispid = self.user.last_jam_locidispid || 0
my_locid = locidispid / 1000000
rel = rel.joins("LEFT JOIN geoiplocations AS my_geo ON my_geo.locid = #{my_locid}")
rel = rel.joins("LEFT JOIN geoiplocations AS other_geo ON other_geo.latitude = bands.lat AND other_geo.longitude = bands.lng")
rel = rel.group("bands.id, my_geo.geog, other_geo.geog")
rel = rel.order('st_distance(my_geo.geog, other_geo.geog)')
elsif 'price_asc' == val
rel = rel.order('gig_minimum ASC')
elsif 'price_desc' == val
rel = rel.order('gig_minimum DESC')
end
rel
end
def do_search(filter)
rel = Band.unscoped
filter.keys.each do |fkey|
mname = "_#{fkey}"
if self.respond_to?(mname)
rel = self.send(mname.to_sym, rel, filter)
end
end
rel
end
def search_includes(rel, subtype=TO_JOIN)
TO_JOIN == subtype ? rel.includes([:instruments]) : rel
end
def _process_results_page(_results)
@results = _results
if user
@user_counters = @results.inject({}) { |hh,val| hh[val.id] = {}; hh }
# this gets counts for each search result
@results.each do |bb|
counters = {
COUNT_FOLLOW => Follow.where(:followable_id => bb.id).count,
COUNT_RECORD => Recording.where(:band_id => bb.id).count,
COUNT_SESSION => MusicSession.where(:band_id => bb.id).count
}
@user_counters[bb.id] = counters
end
else
@user_counters = {}
end
self
end
private
def _count(band, key)
if mm = @user_counters[band.id]
return mm[key]
end if @user_counters
0
end
public
def follow_count(band)
_count(band, COUNT_FOLLOW)
end
def record_count(band)
_count(band, COUNT_RECORD)
end
def session_count(band)
_count(band, COUNT_SESSION)
end
def is_follower?(band)
if mm = @user_counters[band.id]
return mm.include?(RESULT_FOLLOW)
end if @user_counters
false
end
def search_type
self.class.to_s
end
def is_blank?(subtype=TO_JOIN)
self.search_filter_for_subtype(subtype) == self.class.json_schema[subtype]
end
def reset_filter(subtype, data=nil)
data ||= self.class.json_schema[subtype]
dblob = self.data_blob
dblob[subtype] = data
self.data_blob = dblob
self.save
end
def reset_search_results(subtype=TO_JOIN)
reset_filter(subtype)
search_results_page(subtype)
end
def self.search_filter_json(user, subtype=TO_JOIN)
self.user_search_filter(user).json[subtype]
end
def search_filter_for_subtype(subtype)
self.data_blob[subtype]
end
def search_results_page(subtype=TO_JOIN, filter=nil, page=1)
if filter
reset_filter(subtype, filter)
else
filter = self.search_filter_for_subtype(subtype)
end
rel = do_search(filter)
@page_number = [page.to_i, 1].max
rel = rel.paginate(:page => @page_number, :per_page => self.class::PER_PAGE)
rel = self.search_includes(rel, subtype)
@page_count = rel.total_pages
_process_results_page(rel.all)
end
def _add_description(descrip, add)
descrip += "; " if 0 < descrip.length
descrip + add
end
def description(subtype=TO_JOIN)
return '' if self.is_blank?(subtype)
filter = search_filter_for_subtype(subtype)
str = ''
if filter.has_key?(KEY_SORT_ORDER)
str += 'Sort = '
case sort = filter[KEY_SORT_ORDER]
when 'distance'
str += SORT_ORDERS[sort]
when 'latency'
str += SORT_ORDERS[sort]
when 'price_asc'
str += HIRE_SORT_ORDERS[sort]
when 'price_desc'
str += HIRE_SORT_ORDERS[sort]
end
end
if (val = filter[KEY_BAND_TYPE]) != ANY_VAL_STR
str = _add_description(str, "Band type = #{BAND_TYPES[val]}")
end if filter.has_key?(KEY_BAND_TYPE)
if (val = filter[KEY_BAND_STATUS]) != ANY_VAL_STR
str = _add_description(str, "Band status = #{BAND_STATUS[val]}")
end if filter.has_key?(KEY_BAND_STATUS)
if (val = filter[KEY_PLAY_COMMIT]) != ANY_VAL_STR
str = _add_description(str, "Play commitment = #{PLAY_COMMITS[val]}")
end if filter.has_key?(KEY_PLAY_COMMIT)
if (val = filter[KEY_TOUR_OPTION]) != ANY_VAL_STR
str = _add_description(str, "Touring options = #{TOUR_OPTIONS[val]}")
end if filter.has_key?(KEY_TOUR_OPTION)
if (val = filter[KEY_PERF_SAMPLES]) != ANY_VAL_STR
str = _add_description(str, "Performance samples = #{PERF_SAMPLES[val]}")
end if filter.has_key?(KEY_PERF_SAMPLES)
if (val = filter[KEY_HIRE_MAX_COST].to_i) > 0
str = _add_description(str, "Maximum gig cost = $#{val}")
end if filter.has_key?(KEY_HIRE_MAX_COST)
if 0 < filter[KEY_HIRE_FREE]
str = _add_description(str, "Bands playing free gigs")
end if filter.has_key?(KEY_HIRE_FREE)
if (val = filter[KEY_GIGS].to_i) != GIG_COUNTS[0]
str = _add_description(str, "Concert gigs = #{GIG_LABELS[val]}")
end if filter.has_key?(KEY_GIGS)
if 0 < (val = filter[KEY_GENRES]).length
gstr = "Genres = "
genres = Genre.where(["id IN (?)", val]).order('description').pluck(:description)
gstr += genres.join(', ')
str = _add_description(str, gstr)
end if filter.has_key?(KEY_GENRES)
if 0 < ((val = filter[KEY_INSTRUMENTS]) || '').length
istr = "Instruments = "
instr_ids = val.collect { |vv| vv['instrument_id'] }
instrs = Instrument.where(["id IN (?)", instr_ids]).order(:description)
instrs.each_with_index do |ii, idx|
proficiency = val.detect { |vv| vv['instrument_id'] == ii.id }['proficiency_level']
istr += "#{ii.description} (#{INSTRUMENT_PROFICIENCY[proficiency.to_i]})"
istr += ', ' unless idx==(instrs.length-1)
end
str = _add_description(str, istr)
end if filter.has_key?(KEY_INSTRUMENTS)
str = "Current Search: #{str}"
str
end
end
end

View File

@ -0,0 +1,196 @@
module JamRuby
class BaseSearch < JsonStore
attr_accessor :page_count, :results, :page_number
ANY_VAL_STR = 'any'
ANY_VAL_INT = -1
VAL_YES = 'yes'
VAL_NO = 'no'
PER_PAGE = 10
PG_SMALLINT_MAX = 32767
KEY_SKILL = 'skill_level'
KEY_GENRES = 'genres'
KEY_INSTRUMENTS = 'instruments'
KEY_GIGS = 'concert_gigs'
KEY_SORT_ORDER = 'sort_order'
SORT_VALS = %W{ latency distance }
SORT_ORDERS = {
SORT_VALS[0] => 'Latency to Me',
SORT_VALS[1] => 'Distance to Me'
}
SKILL_VALS = [ANY_VAL_INT, 1, 2]
SKILL_LEVELS = {
SKILL_VALS[0] => 'Any',
SKILL_VALS[1] => 'Amateur',
SKILL_VALS[2] => 'Pro',
}
GIG_COUNTS = [ANY_VAL_INT, 0, 1, 2, 3, 4]
GIG_LABELS = {
GIG_COUNTS[0] => 'Any',
GIG_COUNTS[1] => 'under 10',
GIG_COUNTS[2] => '10 to 50',
GIG_COUNTS[3] => '50 to 100',
GIG_COUNTS[4] => 'over 100'
}
INSTRUMENT_PROFICIENCY = {
1 => 'Beginner',
2 => 'Intermediate',
3 => 'Expert',
}
def self.json_schema
{
KEY_SORT_ORDER => self::SORT_VALS[0],
KEY_INSTRUMENTS => [],
KEY_GENRES => [],
KEY_GIGS => self::GIG_COUNTS[0].to_s,
}
end
def self.search_filter_meta(jschema=nil, sort_order=nil)
jschema ||= self.json_schema
schema_keys = jschema.keys
sort_order ||= { keys: self::SORT_VALS, map: self::SORT_ORDERS }
multi_keys = jschema.collect { |kk,vv| vv.is_a?(Array) ? kk : nil }.compact
{
per_page: self::PER_PAGE,
filter_keys: {
keys: schema_keys,
multi: multi_keys,
single: schema_keys - multi_keys,
},
sort_order: sort_order
}
end
RESULT_FOLLOW = :follows
RESULT_FRIEND = :friends
COUNT_FRIEND = :count_friend
COUNT_FOLLOW = :count_follow
COUNT_RECORD = :count_record
COUNT_SESSION = :count_session
COUNTERS = [COUNT_FRIEND, COUNT_FOLLOW, COUNT_RECORD, COUNT_SESSION]
def self.user_search_filter(user)
unless ss = user.send(self.name.demodulize.tableize.singularize)
ss = self.create_search(user)
end
ss
end
def self.search_filter_json(user)
self.user_search_filter(user).json
end
def self.create_search(user)
ms = self.new
ms.user = user
ms.data_blob = self.json_schema
ms.save!
ms
end
def self.search_target_class
end
# FIXME: SQL INJECTION
def _genres(rel, query_data=json)
gids = query_data[KEY_GENRES]
unless gids.blank?
gidsql = gids.join("','")
gpsql = "SELECT player_id FROM genre_players WHERE (player_type = '#{self.class.search_target_class.name}' AND genre_id IN ('#{gidsql}'))"
rel = rel.where("#{self.class.search_target_class.table_name}.id IN (#{gpsql})")
end
rel
end
# FIXME: SQL INJECTION
def _instruments(rel, query_data=json)
unless (instruments = query_data[KEY_INSTRUMENTS]).blank?
instsql = "SELECT player_id FROM musicians_instruments WHERE (("
instsql += instruments.collect do |inst|
"instrument_id = '#{inst['instrument_id']}' AND proficiency_level = #{inst['proficiency_level']}"
end.join(") OR (")
instsql += "))"
rel = rel.where("#{self.class.search_target_class.table_name}.id IN (#{instsql})")
end
rel
end
def _gigs(rel)
gg = json[KEY_GIGS].to_i
rel = rel.where('concert_count = ?',gg) if 0 <= gg
rel
end
def _skills(rel)
if 0 < (val = json[KEY_SKILL].to_i)
rel = rel.where(['skill_level = ?', val])
end
rel
end
def _sort_order(rel)
end
def do_search(params={})
end
def process_results_page(objs)
end
def search_includes(rel)
rel
end
def search_results_page(filter=nil, page=1)
if filter
self.data_blob = filter
self.save
else
filter = self.data_blob
end
rel = do_search(filter)
@page_number = [page.to_i, 1].max
rel = rel.paginate(:page => @page_number, :per_page => self.class::PER_PAGE)
rel = self.search_includes(rel)
@page_count = rel.total_pages
process_results_page(rel.all)
end
def reset_filter
self.data_blob = self.class.json_schema
self.save
end
def reset_search_results
reset_filter
search_results_page
end
def search_type
self.class.to_s
end
def is_blank?
self.data_blob == self.class.json_schema
end
def description
end
end
end

View File

@ -31,6 +31,5 @@ module JamRuby
GenericState.find('default')
end
end
end

View File

@ -19,7 +19,8 @@ module JamRuby
has_and_belongs_to_many :teachers, :class_name => "JamRuby::Teacher", :join_table => "teachers_genres"
# jam tracks
has_many :jam_tracks, :class_name => "JamRuby::JamTrack"
has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "genre_id"
has_many :jam_tracks, :through => :genres_jam_tracks, :class_name => "JamRuby::JamTrack", :source => :genre
def to_s
description

View File

@ -0,0 +1,8 @@
module JamRuby
class GenreJamTrack < ActiveRecord::Base
self.table_name = 'genres_jam_tracks'
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
belongs_to :genre, class_name: 'JamRuby::Genre'
end
end

View File

@ -14,12 +14,12 @@ module JamRuby
attr_accessor :uploading_preview
attr_accessible :name, :description, :bpm, :time_signature, :status, :recording_type,
:original_artist, :songwriter, :publisher, :licensor, :licensor_id, :pro, :genre, :genre_id, :sales_region, :price,
:original_artist, :songwriter, :publisher, :licensor, :licensor_id, :pro, :genres_jam_tracks_attributes, :sales_region, :price,
:reproduction_royalty, :public_performance_royalty, :reproduction_royalty_amount,
:licensor_royalty_amount, :pro_royalty_amount, :plan_code, :initial_play_silence, :jam_track_tracks_attributes,
:jam_track_tap_ins_attributes, :version, :jmep_json, :jmep_text, :pro_ascap, :pro_bmi, :pro_sesac, :duration, as: :admin
:jam_track_tap_ins_attributes, :genre_ids, :version, :jmep_json, :jmep_text, :pro_ascap, :pro_bmi, :pro_sesac, :duration, as: :admin
validates :name, presence: true, uniqueness: true, length: {maximum: 200}
validates :name, presence: true, length: {maximum: 200}
validates :plan_code, presence: true, uniqueness: true, length: {maximum: 50 }
validates :description, length: {maximum: 1000}
validates :time_signature, inclusion: {in: [nil] + [''] + TIME_SIGNATURES} # the empty string is needed because of activeadmin
@ -39,14 +39,17 @@ module JamRuby
validates :public_performance_royalty, inclusion: {in: [nil, true, false]}
validates :duration, numericality: {only_integer: true}, :allow_nil => true
validates_format_of :reproduction_royalty_amount, with: /^\d+\.*\d{0,3}$/
validates_format_of :licensor_royalty_amount, with: /^\d+\.*\d{0,3}$/
validates_format_of :reproduction_royalty_amount, with: /^\d+\.*\d{0,4}$/, :allow_blank => true
validates_format_of :licensor_royalty_amount, with: /^\d+\.*\d{0,4}$/, :allow_blank => true
belongs_to :genre, class_name: "JamRuby::Genre"
belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id'
belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id', :inverse_of => :jam_tracks
has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "jam_track_id"
has_many :genres, :through => :genres_jam_tracks, :class_name => "JamRuby::Genre", :source => :genre
has_many :jam_track_tracks, :class_name => "JamRuby::JamTrackTrack", order: 'track_type ASC, position ASC, part ASC, instrument_id ASC'
has_many :jam_track_tap_ins, :class_name => "JamRuby::JamTrackTapIn", order: 'offset_time ASC'
has_many :jam_track_files, :class_name => "JamRuby::JamTrackFile"
has_many :jam_track_rights, :class_name => "JamRuby::JamTrackRight" #, inverse_of: 'jam_track', :foreign_key => "jam_track_id" # '
@ -67,6 +70,82 @@ module JamRuby
accepts_nested_attributes_for :jam_track_tracks, allow_destroy: true
accepts_nested_attributes_for :jam_track_tap_ins, allow_destroy: true
# we can make sure a few things stay in sync here.
# 1) the reproduction_royalty_amount has to stay in sync based on duration
# 2) the onboarding_exceptions JSON column
after_save :sync_reproduction_royalty
after_save :sync_onboarding_exceptions
def sync_reproduction_royalty
# reproduction royalty table based on duration
# The statutory mechanical royalty rate for permanent digital downloads is:
# 9.10¢ per copy for songs 5 minutes or less, or
# 1.75¢ per minute or fraction thereof, per copy for songs over 5 minutes.
# So the base rate is 9.1 cents for anything up to 5 minutes.
# 5.01 to 6 minutes should be 10.5 cents.
# 6.01 to 7 minutes should be 12.25 cents.
# Etc.
royalty = nil
if self.duration
minutes = (self.duration - 1) / 60
extra_minutes = minutes - 4
extra_minutes = 0 if extra_minutes < 0
royalty = (0.091 + (0.0175 * extra_minutes)).round(5)
end
self.update_column(:reproduction_royalty_amount, royalty)
true
end
def sync_onboarding_exceptions
exceptions = {}
if self.duration.nil?
exceptions[:no_duration] = true
end
if self.genres.count == 0
exceptions[:no_genres] = true
end
if self.year.nil?
exceptions[:no_year] = true
end
if self.licensor.nil?
exceptions[:no_licensor] = true
end
if self.missing_instrument_info?
exceptions[:unknown_instrument] = true
end
if self.master_track.nil?
exceptions[:no_master] = true
end
if missing_previews?
exceptions[:missing_previews] = true
end
if duplicate_positions?
exceptions[:duplicate_positions] = true
end
if exceptions.keys.length == 0
self.update_column(:onboarding_exceptions, nil)
else
self.update_column(:onboarding_exceptions, exceptions.to_json)
end
true
end
def duplicate_positions?
counter = {}
jam_track_tracks.each do |track|
@ -87,6 +166,17 @@ module JamRuby
duplicate
end
def missing_instrument_info?
missing_instrument_info = false
self.jam_track_tracks.each do |track|
if track.instrument_id == 'other' && (track.part == nil || track.part.start_with?('Other'))
missing_instrument_info = true
break
end
end
missing_instrument_info
end
def missing_previews?
missing_preview = false
self.jam_track_tracks.each do |track|
@ -171,7 +261,7 @@ module JamRuby
end
if options[:group_artist]
query = query.select("original_artist, array_agg(jam_tracks.id) AS id, MIN(name) AS name, MIN(description) AS description, MIN(recording_type) AS recording_type, MIN(original_artist) AS original_artist, MIN(songwriter) AS songwriter, MIN(publisher) AS publisher, MIN(sales_region) AS sales_region, MIN(price) AS price, MIN(version) AS version, MIN(genre_id) AS genre_id")
query = query.select("original_artist, array_agg(jam_tracks.id) AS id, MIN(name) AS name, MIN(description) AS description, MIN(recording_type) AS recording_type, MIN(original_artist) AS original_artist, MIN(songwriter) AS songwriter, MIN(publisher) AS publisher, MIN(sales_region) AS sales_region, MIN(price) AS price, MIN(version) AS version")
query = query.group("original_artist")
query = query.order('jam_tracks.original_artist')
else
@ -180,7 +270,12 @@ module JamRuby
end
query = query.where("jam_tracks.status = ?", 'Production') unless user.admin
query = query.where("jam_tracks.genre_id = '#{options[:genre]}'") unless options[:genre].blank?
unless options[:genre].blank?
query = query.joins(:genres)
query = query.where('genre_id = ? ', options[:genre])
end
query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}' and jam_track_tracks.track_type != 'Master'") unless options[:instrument].blank?
query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?
@ -231,7 +326,12 @@ module JamRuby
query = query.order('jam_tracks.original_artist')
query = query.where("jam_tracks.status = ?", 'Production') unless user.admin
query = query.where("jam_tracks.genre_id = '#{options[:genre]}'") unless options[:genre].blank?
unless options[:genre].blank?
query = query.joins(:genres)
query = query.where('genre_id = ? ', options[:genre])
end
query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}'") unless options[:instrument].blank?
query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?

View File

@ -0,0 +1,78 @@
module JamRuby
# holds a click track or precount file
class JamTrackFile < ActiveRecord::Base
include JamRuby::S3ManagerMixin
# there should only be one Master per JamTrack, but there can be N Track per JamTrack
FILE_TYPE = %w{ClickWav ClickTxt Precount}
@@log = Logging.logger[JamTrackFile]
before_destroy :delete_s3_files
attr_accessible :jam_track_id, :file_type, :filename, as: :admin
attr_accessible :url, :md5, :length, as: :admin
attr_accessor :original_audio_s3_path, :skip_uploader, :preview_generate_error
before_destroy :delete_s3_files
validates :file_type, inclusion: {in: FILE_TYPE }
belongs_to :jam_track, class_name: "JamRuby::JamTrack"
# create storage directory that will house this jam_track, as well as
def store_dir
"jam_track_files"
end
# create name of the file
def filename(original_name)
"#{store_dir}/#{jam_track.original_artist}/#{jam_track.name}/#{original_name}"
end
def manually_uploaded_filename
if click_wav?
filename('click.wav')
elsif click_txt?
filename('click.txt')
elsif precount?
filename('precount.wav')
else
raise 'unknown file type: ' + file_type
end
end
def click_wav?
track_type == 'ClickWav'
end
def click_txt?
track_type == 'ClickTxt'
end
def precount?
track_type == 'Precount'
end
# creates a short-lived URL that has access to the object.
# the idea is that this is used when a user who has the rights to this tries to download this JamTrack
# we would verify their rights (can_download?), and generates a URL in response to the click so that they can download
# but the url is short lived enough so that it wouldn't be easily shared
def sign_url(expiration_time = 120)
s3_manager.sign_url(self[url], {:expires => expiration_time, :response_content_type => 'audio/wav', :secure => true})
end
def can_download?(user)
# I think we have to make a special case for 'previews', but maybe that's just up to the controller to not check can_download?
jam_track.owners.include?(user)
end
def delete_s3_files
s3_manager.delete(self[:url]) if self[:url] && s3_manager.exists?(self[:url])
end
end
end

View File

@ -1,6 +1,8 @@
module JamRuby
class JamTrackLicensor < ActiveRecord::Base
table_name = 'jam_track_licensors'
attr_accessible :name, :description, :attention, :address_line_1, :address_line_2,
:city, :state, :zip_code, :contact, :email, :phone, as: :admin
@ -16,6 +18,6 @@ module JamRuby
validates :email, length: {maximum: 200}
validates :phone, length: {maximum: 200}
has_many :jam_tracks, :class_name => "JamRuby::JamTrack", foreign_key: 'licensor_id'
has_many :jam_tracks, :class_name => "JamRuby::JamTrack", foreign_key: 'licensor_id', :inverse_of => :licensor
end
end

View File

@ -24,7 +24,7 @@ module JamRuby
before_destroy :delete_s3_files
validates :position, presence: true, numericality: {only_integer: true}, length: {in: 1..1000}
validates :part, length: {maximum: 25}
validates :part, length: {maximum: 35}
validates :track_type, inclusion: {in: TRACK_TYPE }
validates :preview_start_time, numericality: {only_integer: true}, length: {in: 1..1000}, :allow_nil => true
validates_uniqueness_of :part, scope: [:jam_track_id, :instrument_id]
@ -131,22 +131,36 @@ module JamRuby
end
def generate_preview
begin
Dir.mktmpdir do |tmp_dir|
input = File.join(tmp_dir, 'in.ogg')
output = File.join(tmp_dir, 'out.ogg')
output_mp3 = File.join(tmp_dir, 'out.mp3')
start = self.preview_start_time.to_f / 1000
stop = start + 20
raise 'no track' unless self["url_44"]
s3_manager.download(self.url_by_sample_rate(44), input)
process_preview(input, tmp_dir)
end
rescue Exception => e
@@log.error("error in sox command #{e.to_s}")
@preview_generate_error = e.to_s
end
end
# input is the original ogg file for the track. tmp_dir is where this code can safely generate output stuff and have it cleaned up later
def process_preview(input, tmp_dir)
uuid = SecureRandom.uuid
output = File.join(tmp_dir, "#{uuid}.ogg")
output_mp3 = File.join(tmp_dir, "#{uuid}.mp3")
start = self.preview_start_time.to_f / 1000
stop = start + 20
command = "sox \"#{input}\" \"#{output}\" trim #{sprintf("%.3f", start)} =#{sprintf("%.3f", stop)}"
@@log.debug("trimming using: " + command)
@ -207,12 +221,6 @@ module JamRuby
end
end
end
rescue Exception => e
@@log.error("error in sox command #{e.to_s}")
@preview_generate_error = e.to_s
end
end
private

View File

@ -1,22 +1,12 @@
module JamRuby
class MusicianSearch < JsonStore
class MusicianSearch < BaseSearch
attr_accessor :page_count, :results, :user_counters, :page_number
cattr_accessor :jschema, :search_meta
attr_accessor :user_counters
ANY_VAL_STR = 'any'
ANY_VAL_INT = -1
PER_PAGE = 10
PG_SMALLINT_MAX = 32767
KEY_GIGS = 'concert_gigs'
KEY_STUDIOS = 'studio_sessions'
KEY_AGES = 'ages'
KEY_SKILL = 'skill_level'
KEY_GENRES = 'genres'
KEY_INSTRUMENTS = 'instruments'
KEY_INTERESTS = 'interests'
KEY_SORT_ORDER = 'sort_order'
SORT_VALS = %W{ latency distance }
SORT_ORDERS = {
@ -24,22 +14,6 @@ module JamRuby
SORT_VALS[1] => 'Distance to Me'
}
SKILL_VALS = [ANY_VAL_INT, 1, 2]
SKILL_LEVELS = {
SKILL_VALS[0] => 'Any',
SKILL_VALS[1] => 'Amateur',
SKILL_VALS[2] => 'Pro',
}
GIG_COUNTS = [ANY_VAL_INT, 0, 1, 2, 3, 4]
GIG_LABELS = {
GIG_COUNTS[0] => 'Any',
GIG_COUNTS[1] => 'under 10',
GIG_COUNTS[2] => '10 to 50',
GIG_COUNTS[3] => '50 to 100',
GIG_COUNTS[4] => 'over 100'
}
STUDIO_COUNTS = [ANY_VAL_INT, 0, 1, 2, 3, 4]
STUDIOS_LABELS = {
STUDIO_COUNTS[0] => 'Any',
@ -74,79 +48,26 @@ module JamRuby
INTEREST_VALS[5] => 'Co-Writing'
}
INSTRUMENT_PROFICIENCY = {
1 => 'Beginner',
2 => 'Intermediate',
3 => 'Expert',
}
JSON_SCHEMA = {
KEY_SORT_ORDER => SORT_VALS[0],
KEY_INSTRUMENTS => [],
def self.json_schema
return @@jschema if @@jschema
@@jschema = BaseSearch.json_schema.merge({
KEY_INTERESTS => INTEREST_VALS[0],
KEY_GENRES => [],
KEY_GIGS => GIG_COUNTS[0].to_s,
KEY_STUDIOS => STUDIO_COUNTS[0].to_s,
KEY_SKILL => SKILL_VALS[0].to_s,
KEY_AGES => []
}
JSON_SCHEMA_KEYS = JSON_SCHEMA.keys
MULTI_VALUE_KEYS = JSON_SCHEMA.collect { |kk,vv| vv.is_a?(Array) ? kk : nil }.compact
SINGLE_VALUE_KEYS = JSON_SCHEMA.keys - MULTI_VALUE_KEYS
KEY_AGES => [],
KEY_SKILL => self::SKILL_VALS[0].to_s,
})
end
SEARCH_FILTER_META = {
per_page: PER_PAGE,
filter_keys: {
keys: JSON_SCHEMA_KEYS,
multi: MULTI_VALUE_KEYS,
single: SINGLE_VALUE_KEYS,
},
sort_order: { keys: SORT_VALS, map: SORT_ORDERS },
def self.search_filter_meta
return @@search_meta if @@search_meta
@@search_meta = super.merge({
interests: { keys: INTEREST_VALS, map: INTERESTS },
ages: { keys: AGE_COUNTS, map: AGES }
}
def self.user_search_filter(user)
unless ms = user.musician_search
ms = self.create_search(user)
end
ms
})
end
def self.search_filter_json(user)
self.user_search_filter(user).json
end
def self.create_search(user)
ms = self.new
ms.user = user
ms.data_blob = JSON_SCHEMA
ms.save!
ms
end
# XXX SQL INJECTION
def _genres(rel)
gids = json[KEY_GENRES]
unless gids.blank?
gidsql = gids.join("','")
gpsql = "SELECT player_id FROM genre_players WHERE (player_type = 'JamRuby::User' AND genre_id IN ('#{gidsql}'))"
rel = rel.where("users.id IN (#{gpsql})")
end
rel
end
# XXX SQL INJECTION
def _instruments(rel)
unless (instruments = json['instruments']).blank?
instsql = "SELECT player_id FROM musicians_instruments WHERE (("
instsql += instruments.collect do |inst|
"instrument_id = '#{inst['id']}' AND proficiency_level = #{inst['level']}"
end.join(") OR (")
instsql += "))"
rel = rel.where("users.id IN (#{instsql})")
end
rel
def self.search_target_class
User
end
def _ages(rel)
@ -193,7 +114,7 @@ module JamRuby
def _skills(rel)
if 0 < (val = json[KEY_SKILL].to_i)
rel = rel.where(['skill_level = ?', val])
rel = rel.where(skill_level: val)
end
rel
end
@ -207,8 +128,8 @@ module JamRuby
end
def _sort_order(rel)
val = json[KEY_SORT_ORDER]
if SORT_VALS[1] == val
val = json[self.class::KEY_SORT_ORDER]
if self.class::SORT_VALS[1] == val
locidispid = self.user.last_jam_locidispid || 0
my_locid = locidispid / 1000000
rel = rel.joins("LEFT JOIN geoiplocations AS my_geo ON my_geo.locid = #{my_locid}")
@ -235,45 +156,11 @@ module JamRuby
rel
end
def search_results_page(filter=nil, page=1)
if filter
self.data_blob = filter
self.save
else
filter = self.data_blob
def search_includes(rel)
rel.includes([:instruments, :followings, :friends])
end
rel = do_search(filter)
@page_number = [page.to_i, 1].max
rel = rel.paginate(:page => @page_number, :per_page => PER_PAGE)
rel = rel.includes([:instruments, :followings, :friends])
@page_count = rel.total_pages
musician_results(rel.all)
end
def reset_filter
self.data_blob = JSON_SCHEMA
self.save
end
def reset_search_results
reset_filter
search_results_page
end
RESULT_FOLLOW = :follows
RESULT_FRIEND = :friends
COUNT_FRIEND = :count_friend
COUNT_FOLLOW = :count_follow
COUNT_RECORD = :count_record
COUNT_SESSION = :count_session
COUNTERS = [COUNT_FRIEND, COUNT_FOLLOW, COUNT_RECORD, COUNT_SESSION]
def musician_results(_results)
def process_results_page(_results)
@results = _results
@user_counters = {} and return self unless user
@ -351,7 +238,7 @@ module JamRuby
end
def is_blank?
self.data_blob == JSON_SCHEMA
self.data_blob == self.class.json_schema
end
def description

View File

@ -173,6 +173,7 @@ module JamRuby
has_many :performance_samples, :class_name => "JamRuby::PerformanceSample", :foreign_key=> 'player_id'
has_one :musician_search, :class_name => 'JamRuby::MusicianSearch'
has_one :band_search, :class_name => 'JamRuby::BandSearch'
before_save :create_remember_token, :if => :should_validate_password?
before_save :stringify_avatar_info , :if => :updating_avatar

View File

@ -750,7 +750,7 @@ FactoryGirl.define do
licensor_royalty_amount 0.999
sequence(:plan_code) { |n| "jamtrack-#{n}" }
genre JamRuby::Genre.first
genres [JamRuby::Genre.first]
association :licensor, factory: :jam_track_licensor
factory :jam_track_with_tracks do

View File

@ -22,11 +22,13 @@ describe JamTrackImporter do
in_directory_with_file(metafile)
before(:each) do
JamTrackImporter.storage_format = 'default'
content_for_file(YAML.dump(sample_yml))
end
it "no meta" do
s3_metalocation = 'audio/Artist 1/Bogus Place/meta.yml'
JamTrackImporter.storage_format = 'default'
JamTrackImporter.load_metalocation(s3_metalocation).should be_nil
end
@ -38,9 +40,105 @@ describe JamTrackImporter do
end
end
describe "sort_tracks" do
let(:jam_track) { FactoryGirl.create(:jam_track) }
let(:importer) { JamTrackImporter.new() }
let(:vocal) {Instrument.find('voice')}
let(:drums) {Instrument.find('drums')}
let(:bass_guitar) {Instrument.find('bass guitar')}
let(:piano) {Instrument.find('piano')}
let(:keyboard) {Instrument.find('keyboard')}
let(:acoustic_guitar) {Instrument.find('acoustic guitar')}
let(:electric_guitar) {Instrument.find('electric guitar')}
let(:other) {Instrument.find('other')}
it "the big sort" do
# specified in https://jamkazam.atlassian.net/browse/VRFS-3296
vocal_lead = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: vocal, part: 'Lead')
vocal_lead_female = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: vocal, part: 'Lead Female')
vocal_lead_male = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: vocal, part: 'Lead Male')
vocal_backing = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: vocal, part: 'Backing')
vocal_random = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: vocal, part: 'Random')
drums_drums = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: drums, part: 'Drums')
drums_percussion = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: drums, part: 'Percussion')
drums_random_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: drums, part: 'A')
drums_random_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: drums, part: 'C')
bass_guitar_bass = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: bass_guitar, part: 'Bass')
bass_guitar_random_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: bass_guitar, part: 'some bass')
bass_guitar_random_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: bass_guitar, part: 'zome bass')
piano_piano = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: piano, part: 'Piano')
keyboard_synth_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: keyboard, part: 'Synth 1')
keyboard_synth_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: keyboard, part: 'Synth 2')
keyboard_pads = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: keyboard, part: 'Pads')
keyboard_random_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: keyboard, part: 'A')
keyboard_random_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: keyboard, part: 'Z')
acoust_guitar_lead = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Lead')
acoust_guitar_lead_x = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Lead X')
acoust_guitar_solo_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Solo 1')
acoust_guitar_solo_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Solo 2')
acoust_guitar_rhythm = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Rhythm')
acoust_guitar_random_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'A')
acoust_guitar_random_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Z')
elect_guitar_lead = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Lead')
elect_guitar_lead_x = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Lead X')
elect_guitar_solo_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Solo 1')
elect_guitar_solo_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Solo 2')
elect_guitar_rhythm = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Rhythm')
elect_guitar_random_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'A')
elect_guitar_random_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Z')
other_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: other, part: 'Other 1')
other_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: other, part: 'Other 2')
expected = [
vocal_lead,
vocal_lead_female,
vocal_lead_male,
vocal_backing,
vocal_random,
drums_drums,
drums_percussion,
drums_random_1,
drums_random_2,
bass_guitar_bass,
piano_piano,
keyboard_synth_1,
keyboard_synth_2,
keyboard_pads,
keyboard_random_1,
keyboard_random_2,
acoust_guitar_lead,
acoust_guitar_lead_x,
acoust_guitar_rhythm,
acoust_guitar_random_1,
acoust_guitar_solo_1,
acoust_guitar_solo_2,
acoust_guitar_random_2,
elect_guitar_lead,
elect_guitar_lead_x,
elect_guitar_solo_1,
elect_guitar_solo_2,
elect_guitar_rhythm,
elect_guitar_random_1,
elect_guitar_random_2,
bass_guitar_random_1,
bass_guitar_random_2,
other_1,
other_2
]
shuffled = expected.shuffle
sorted_tracks = importer.sort_tracks(shuffled)
importer.set_custom_weight(vocal_lead).should eq(100)
expected.each_with_index do |expected_track, i|
sorted_tracks[i].should eq(expected_track)
end
end
end
describe "synchronize" do
let(:jam_track) { JamTrack.new }
let(:importer) { JamTrackImporter.new }
let(:importer) { JamTrackImporter.new() }
let(:minimum_meta) { nil }
let(:metalocation) { 'audio/Artist 1/Song 1/meta.yml' }
let(:options) {{ skip_audio_upload:true }}
@ -64,7 +162,7 @@ describe JamTrackImporter do
describe "parse_wav" do
it "Guitar" do
result = JamTrackImporter.new.parse_wav('blah/Ready for Love Stem - Guitar - Main.wav')
result = JamTrackImporter.new.parse_file('blah/Ready for Love Stem - Guitar - Main.wav')
result[:instrument].should eq('electric guitar')
result[:part].should eq('Main')
end

View File

@ -1,6 +1,19 @@
require 'spec_helper'
describe 'Band search' do
describe 'Band Search Model' do
let!(:searcher) { FactoryGirl.create(:austin_user) }
let!(:search) { BandSearch.user_search_filter(searcher) }
let!(:austin_user) { FactoryGirl.create(:austin_user) }
let!(:dallas_user) { FactoryGirl.create(:dallas_user) }
let!(:miami_user) { FactoryGirl.create(:miami_user) }
let!(:seattle_user) { FactoryGirl.create(:seattle_user) }
let!(:user_types) { [:austin_user, :dallas_user, :miami_user, :seattle_user] }
let!(:to_join) { search.search_filter_for_subtype(BandSearch::TO_JOIN) }
let!(:to_hire) { search.search_filter_for_subtype(BandSearch::TO_HIRE) }
before(:all) do
Recording.delete_all
@ -20,213 +33,310 @@ describe 'Band search' do
FactoryGirl.create(:band_musician, :band => bb, :user => FactoryGirl.create(:user))
end
end
end
context 'default filter settings' do
describe "creates search obj" do
it "finds all bands" do
# expects all the bands
num = Band.count
results = Search.band_filter({ :per_page => num })
expect(results.results.count).to eq(num)
before(:all) do
User.delete_all
end
it "finds bands with proper ordering" do
# the ordering should be create_at since no followers exist
expect(Follow.count).to eq(0)
results = Search.band_filter({ :per_page => Band.count })
rbands = @bands.reverse
results.results.each_with_index do |uu, idx|
expect(uu.id).to eq(@bands.reverse[idx].id)
end
it "associates to user" do
expect(search.user).to eq(searcher)
searcher.reload
expect(searcher.band_search).to eq(search)
end
it "sorts bands by followers" do
users = []
4.downto(1) { |nn| users << FactoryGirl.create(:user) }
users.each_with_index do |u, index|
if index != 0
f1 = Follow.new
f1.user = u
f1.followable = @band4
f1.save
end
it "sets json" do
expect(search.search_filter_for_subtype(BandSearch::TO_JOIN)).to eq(BandSearch.json_schema[BandSearch::TO_JOIN])
expect(search.search_filter_for_subtype(BandSearch::TO_HIRE)).to eq(BandSearch.json_schema[BandSearch::TO_HIRE])
end
users.each_with_index do |u, index|
if index != 0
f1 = Follow.new
f1.user = u
f1.followable = @band3
f1.save
end
it "loads all bands by default" do
search.search_results_page
expect(search.results.count).to eq(Band.count)
end
f1 = Follow.new
f1.user = users.first
f1.followable = @band2
f1.save
# establish sorting order
# @band4.followers.concat(users[1..-1])
# @band3.followers.concat(users[1..3])
# @band2.followers.concat(users[0])
@bands.map(&:reload)
expect(@band4.followers.count).to be 3
expect(Follow.count).to be 7
# refresh the order to ensure it works right
users.each_with_index do |u, index|
if index != 0
f1 = Follow.new
f1.user = u
f1.followable = @band2
f1.save
end
end
# @band2.followers.concat(users[1..-1])
results = Search.band_filter({ :per_page => @bands.size }, users[0])
expect(results.results[0].id).to eq(@band2.id)
# check the follower count for given entry
expect(results.results[0].search_follow_count.to_i).not_to eq(0)
# check the follow relationship between current_user and result
expect(results.is_follower?(@band2)).to be true
end
it 'paginates properly' do
# make sure pagination works right
params = { :per_page => 2, :page => 1 }
results = Search.band_filter(params)
expect(results.results.count).to be 2
it "has follower counts" do
Follow.create(user_id: searcher.id, followable: Band.first)
search.search_results_page
expect(search.follow_count(Band.first)).to eq(1)
end
end
def make_session(band)
usr = band.users[0]
session = FactoryGirl.create(:active_music_session, :creator => usr, :description => "Session", :band => band)
FactoryGirl.create(:connection, :user => usr, :music_session => session)
user = FactoryGirl.create(:user)
session
describe "generates description" do
def check_description(_filter, subtype, key, value, lookup, label)
_filter[key] = value
search.search_results_page(subtype, _filter)
expect(search.description(subtype)).to match(/Current Search: Sort = .*; #{label} = #{lookup[value]}$/)
end
context 'band stat counters' do
it "follow stat shows follower count" do
users = []
2.downto(1) { |nn| users << FactoryGirl.create(:user) }
users.each do |u|
f1 = Follow.new
f1.user = u
f1.followable = @band1
f1.save
it "renders no description for blank" do
search.search_results_page
expect(search.description).to eq('')
end
# establish sorting order
# @band1.followers.concat(users)
results = Search.band_filter({},@band1)
uu = results.results.detect { |mm| mm.id == @band1.id }
expect(uu).to_not be_nil
expect(results.follow_count(uu)).to eq(users.count)
it "renders description for sort order" do
to_join[BandSearch::KEY_TOUR_OPTION] = BandSearch::VAL_YES
search.search_results_page(BandSearch::TO_JOIN, to_join)
search.search_results_page
expect(search.description).to match(/Sort =/)
end
it "session stat shows session count" do
make_session(@band1)
@band1.reload
results = Search.band_filter({},@band1)
uu = results.results.detect { |mm| mm.id == @band1.id }
expect(uu).to_not be_nil
expect(results.session_count(uu)).to be 1
context "to_join" do
it "renders description for band type" do
check_description(to_join,
BandSearch::TO_JOIN,
BandSearch::KEY_BAND_TYPE,
BandSearch::BAND_TYPE_VAL_STRS[1],
BandSearch::BAND_TYPES,
'Band type')
end
it "renders description for band status" do
check_description(to_join,
BandSearch::TO_JOIN,
BandSearch::KEY_BAND_STATUS,
BandSearch::SKILL_VALS[1],
BandSearch::BAND_STATUS,
'Band status')
end
it "renders description for concert gigs" do
check_description(to_join,
BandSearch::TO_JOIN,
BandSearch::KEY_GIGS,
BandSearch::GIG_COUNTS[1],
BandSearch::GIG_LABELS,
'Concert gigs')
end
it "renders description for play commitment" do
check_description(to_join,
BandSearch::TO_JOIN,
BandSearch::KEY_PLAY_COMMIT,
BandSearch::PLAY_COMMIT_VALS[1],
BandSearch::PLAY_COMMITS,
'Play commitment')
end
it "renders description for tour option" do
check_description(to_join,
BandSearch::TO_JOIN,
BandSearch::KEY_TOUR_OPTION,
BandSearch::VAL_YES,
BandSearch::TOUR_OPTIONS,
'Touring options')
end
it "renders description for genres" do
to_join[BandSearch::KEY_GENRES] = [Genre.first.id]
search.search_results_page(BandSearch::TO_JOIN, to_join)
expect(search.description).to match(/Current Search: Sort = .*; Genres = #{Genre.first.description}$/)
end
it "renders description for instruments" do
to_join[BandSearch::KEY_INSTRUMENTS] = [{ 'instrument_id' => Instrument.first.id, 'proficiency_level' => 1 }]
search.search_results_page(BandSearch::TO_JOIN, to_join)
expect(search.description).to match(/Current Search: Sort = .*; Instruments = #{Instrument.first.description} \(#{BandSearch::INSTRUMENT_PROFICIENCY[1]}\)$/)
end
end
context 'band sorting' do
it "by plays" do
make_session(@band2)
make_session(@band2)
make_session(@band2)
make_session(@band1)
# order results by num recordings
results = Search.band_filter({ :orderby => 'plays' })
expect(results.results[0].id).to eq(@band2.id)
expect(results.results[1].id).to eq(@band1.id)
context "to_hire" do
it "renders description for genres" do
to_hire[BandSearch::KEY_GENRES] = [Genre.first.id]
search.search_results_page(BandSearch::TO_HIRE, to_hire)
expect(search.description(BandSearch::TO_HIRE)).to match(/Current Search: Sort = .*; Genres = #{Genre.first.description}$/)
end
it "by now playing" do
# should get 1 result with 1 active session
session = make_session(@band3)
#FactoryGirl.create(:active_music_session, :music_session => session)
it "renders description for band status" do
check_description(to_hire,
BandSearch::TO_HIRE,
BandSearch::KEY_BAND_STATUS,
BandSearch::BAND_STATUS_VALS[1],
BandSearch::BAND_STATUS,
'Band status')
end
results = Search.band_filter({ :orderby => 'playing' })
expect(results.results.count).to be 1
expect(results.results.first.id).to eq(@band3.id)
it "renders description for concert gigs" do
check_description(to_hire,
BandSearch::TO_HIRE,
BandSearch::KEY_GIGS,
BandSearch::GIG_COUNTS[1],
BandSearch::GIG_LABELS,
'Concert gigs')
end
# should get 2 results with 2 active sessions
# sort order should be created_at DESC
session = make_session(@band4)
#FactoryGirl.create(:active_music_session, :music_session => session)
results = Search.band_filter({ :orderby => 'playing' })
expect(results.results.count).to be 2
expect(results.results[0].id).to eq(@band4.id)
expect(results.results[1].id).to eq(@band3.id)
it "renders description for performance samples" do
check_description(to_hire,
BandSearch::TO_HIRE,
BandSearch::KEY_PERF_SAMPLES,
BandSearch::PERF_SAMPLES_VALS[1],
BandSearch::PERF_SAMPLES,
'Performance samples')
end
it "renders description max cost" do
to_hire[BandSearch::KEY_HIRE_MAX_COST] = 100
search.search_results_page(BandSearch::TO_HIRE, to_hire)
expect(search.description(BandSearch::TO_HIRE)).to match(/Current Search: Sort = .*; Maximum gig cost = \$100$/)
end
it "renders description free gigs" do
to_hire[BandSearch::KEY_HIRE_FREE] = 1
search.search_results_page(BandSearch::TO_HIRE, to_hire)
expect(search.description(BandSearch::TO_HIRE)).to match(/Current Search: Sort = .*; Bands playing free gigs$/)
end
end
end
describe "filtering by keys" do
let!(:band) { Band.first }
context 'all search keys' do
let!(:filter) { to_join }
it "filters by gigs" do
band.update_attribute(:concert_count, BandSearch::GIG_COUNTS[2])
filter[BandSearch::KEY_GIGS] = BandSearch::GIG_COUNTS[2]
search.search_results_page(BandSearch::TO_JOIN, filter)
expect(search.results.count).to eq(1)
expect(search.results[0].id).to eq(band.id)
end
it "filters by genre" do
band_id = band.id
filter[BandSearch::KEY_GENRES] = [band_id]
search.search_results_page(BandSearch::TO_JOIN, filter)
expect(search.results.count).to eq(Band.all.map(&:genres).flatten.select { |bb| bb.id == band_id }.count)
end
it "filters by band_type" do
band.update_attribute(:band_type, BandSearch::BAND_TYPE_VAL_STRS[1])
filter[BandSearch::KEY_BAND_TYPE] = BandSearch::BAND_TYPE_VAL_STRS[1]
search.search_results_page(BandSearch::TO_JOIN, filter)
expect(search.results.count).to eq(1)
expect(search.results[0].id).to eq(band.id)
end
it "filters by instruments" do
minst = FactoryGirl.create(:musician_instrument)
band.musician_instruments << minst
band.save
filter[BandSearch::KEY_INSTRUMENTS] = [{'instrument_id' => minst.instrument_id,
'proficiency_level' => minst.proficiency_level
}]
search.search_results_page(BandSearch::TO_JOIN, filter)
expect(search.results.count).to eq(1)
expect(search.results[0].id).to eq(band.id)
filter[BandSearch::KEY_INSTRUMENTS] = [{'instrument_id' => minst.instrument_id,
'proficiency_level' => minst.proficiency_level + 1
}]
search.search_results_page(BandSearch::TO_JOIN, filter)
expect(search.results.count).to eq(0)
end
end
context 'to_join' do
let!(:filter) { to_join }
it "sorts by distance" do
bands = Band.all.reverse
bb = bands.first
bb.lat, bb.lng = austin_geoip[:geoiplocation].latitude, austin_geoip[:geoiplocation].longitude
bb.save!
bb = bands.second
bb.lat, bb.lng = dallas_geoip[:geoiplocation].latitude, dallas_geoip[:geoiplocation].longitude
bb.save!
bb = bands.third
bb.lat, bb.lng = miami_geoip[:geoiplocation].latitude, miami_geoip[:geoiplocation].longitude
bb.save!
bb = bands.fourth
bb.lat, bb.lng = seattle_geoip[:geoiplocation].latitude, seattle_geoip[:geoiplocation].longitude
bb.save!
filter[BandSearch::KEY_SORT_ORDER] = BandSearch::SORT_VALS[0]
search.search_results_page(BandSearch::TO_JOIN, filter)
expect(search.results.count).to eq(Band.count)
expect(search.results.first.id).to eq(bands.first.id)
expect(search.results.second.id).to eq(bands.second.id)
expect(search.results.third.id).to eq(bands.third.id)
expect(search.results.fourth.id).to eq(bands.fourth.id)
end
it "filters by play commitment" do
band.update_attribute(BandSearch::KEY_PLAY_COMMIT, BandSearch::PLAY_COMMIT_VALS[1].to_i)
filter[BandSearch::KEY_PLAY_COMMIT] = BandSearch::PLAY_COMMIT_VALS[1]
search.search_results_page(BandSearch::TO_JOIN, filter)
expect(search.results.count).to eq(1)
expect(search.results[0].id).to eq(band.id)
end
it "filters by tour option" do
band.update_attribute(BandSearch::KEY_TOUR_OPTION, true)
filter[BandSearch::KEY_TOUR_OPTION] = BandSearch::VAL_YES
search.search_results_page(BandSearch::TO_JOIN, filter)
expect(search.results.count).to eq(1)
expect(search.results[0].id).to eq(band.id)
end
end
context 'filter settings' do
it "searches bands for a genre" do
genre = FactoryGirl.create(:genre)
@band1.genres << genre
@band1.reload
ggg = @band1.genres.detect { |gg| gg.id == genre.id }
expect(ggg).to_not be_nil
results = Search.band_filter({ :genre => ggg.id })
results.results.each do |rr|
expect(rr.genres.detect { |gg| gg.id==ggg.id }.id).to eq(genre.id)
end
expect(results.results.count).to be 1
context 'to_hire' do
let!(:filter) { to_hire }
it "filters by free gigs" do
band.update_attribute(BandSearch::KEY_HIRE_FREE, true)
filter[BandSearch::KEY_HIRE_FREE] = BandSearch::VAL_YES
search.search_results_page(BandSearch::TO_HIRE, filter)
expect(search.results.count).to eq(1)
expect(search.results[0].id).to eq(band.id)
end
it "finds bands within a given distance of given location" do
pending 'distance search changes'
num = Band.count
expect(@band1.lat).to_not be_nil
# short distance
results = Search.band_filter({ :per_page => num,
:distance => 10,
:city => 'Apex' }, @band1)
expect(results.results.count).to be num
# long distance
results = Search.band_filter({ :per_page => num,
:distance => 1000,
:city => 'Miami',
:state => 'FL' }, @band1)
expect(results.results.count).to be num
it "filters by max cost" do
band.update_attribute(:gig_minimum, 10)
filter[BandSearch::KEY_HIRE_MAX_COST] = 5
search.search_results_page(BandSearch::TO_HIRE, filter)
expect(search.results.count).to eq(0)
filter[BandSearch::KEY_HIRE_MAX_COST] = 15
search.search_results_page(BandSearch::TO_HIRE, filter)
expect(search.results.count).to eq(1)
end
it "finds bands within a given distance of bands location" do
pending 'distance search changes'
expect(@band1.lat).to_not be_nil
# uses the location of @band1
results = Search.band_filter({ :distance => 10, :per_page => Band.count }, @band1)
expect(results.results.count).to be Band.count
it "filters by perform samples" do
filter[BandSearch::KEY_PERF_SAMPLES] = BandSearch::VAL_YES
search.search_results_page(BandSearch::TO_HIRE, filter)
expect(search.results.count).to eq(0)
filter[BandSearch::KEY_PERF_SAMPLES] = BandSearch::VAL_NO
search.search_results_page(BandSearch::TO_HIRE, filter)
expect(search.results.count).to eq(Band.count)
ps = PerformanceSample.new
ps.player = band
ps.service_type = 'youtube'
ps.service_id = 'abc123'
ps.save
# PerformanceSample.create(player: band, service_type: 'youtube', service_id: 'abc123')
filter[BandSearch::KEY_PERF_SAMPLES] = BandSearch::VAL_YES
search.search_results_page(BandSearch::TO_HIRE, filter)
expect(search.results.count).to eq(1)
expect(search.results[0].id).to eq(band.id)
end
it "finds no bands within a given distance of location" do
pending 'distance search changes'
expect(@band1.lat).to_not be_nil
results = Search.band_filter({ :distance => 10, :city => 'San Francisco' }, @band1)
expect(results.results.count).to be 0
end
end

View File

@ -12,6 +12,90 @@ describe JamTrack do
jam_track = FactoryGirl.create(:jam_track)
jam_track.licensor.should_not be_nil
jam_track.licensor.jam_tracks.should == [jam_track]
jam_track.genres.length.should eq(1)
end
describe 'sync_reproduction_royalty' do
it "all possible conditions" do
jam_track = FactoryGirl.create(:jam_track)
jam_track.reproduction_royalty_amount.should be_nil
jam_track.duration = 0
jam_track.save!
jam_track.reproduction_royalty_amount.to_f.should eq(0.091)
jam_track.duration = 1
jam_track.save!
jam_track.reproduction_royalty_amount.to_f.should eq(0.091)
jam_track.duration = 5 * 60 - 1 # just under 5 minutes
jam_track.save!
jam_track.reproduction_royalty_amount.to_f.should eq(0.091)
jam_track.duration = 5 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.to_f.should eq(0.091)
jam_track.duration = 6 * 60 - 1 # just under 6 minutes
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175)
jam_track.duration = 6 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175)
jam_track.duration = 7 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 2)
jam_track.duration = 7 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 2)
jam_track.duration = 8 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 3)
jam_track.duration = 8 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 3)
jam_track.duration = 9 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 4)
jam_track.duration = 9 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 4)
jam_track.duration = 10 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 5)
jam_track.duration = 10 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 5)
jam_track.duration = 11 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 6)
jam_track.duration = 11 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 6)
jam_track.duration = 12 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 7)
jam_track.duration = 12 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 7)
jam_track.duration = 13 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 8)
end
end
describe 'plays' do
@ -98,6 +182,26 @@ describe JamTrack do
query[1].should eq(jam_track1)
end
it "queries on genre" do
jam_track1 = FactoryGirl.create(:jam_track_with_tracks, original_artist: 'artist', name: 'a')
jam_track2 = FactoryGirl.create(:jam_track_with_tracks, original_artist: 'artist', name: 'b')
jam_track1.genres = [Genre.find('rock')]
jam_track2.genres = [Genre.find('asian')]
jam_track1.save!
jam_track2.save!
query, pager = JamTrack.index({genre: 'rock'}, user)
query.size.should == 1
query[0].should eq(jam_track1)
query, pager = JamTrack.index({genre: 'asian'}, user)
query.size.should == 1
query[0].should eq(jam_track2)
query, pager = JamTrack.index({genre: 'african'}, user)
query.size.should == 0
end
it "supports showing purchased only" do
jam_track1 = FactoryGirl.create(:jam_track_with_tracks, name: 'a')
@ -170,7 +274,7 @@ describe JamTrack do
end
it "100.1234" do
jam_track = FactoryGirl.build(:jam_track, reproduction_royalty_amount: 100.1234)
jam_track = FactoryGirl.build(:jam_track, reproduction_royalty_amount: 100.12345)
jam_track.valid?.should be_false
jam_track.errors[:reproduction_royalty_amount].should == ['is invalid']
end

View File

@ -7,6 +7,7 @@ describe JamTrackTrack do
it "created" do
jam_track_track = FactoryGirl.create(:jam_track_track)
jam_track_track.jam_track.should_not be_nil
jam_track_track.jam_track.reload
jam_track_track.jam_track.jam_track_tracks.should == [jam_track_track]
end

View File

@ -115,7 +115,8 @@ group :development, :test do
gem 'test-unit'
# gem 'teaspoon'
# gem 'teaspoon-jasmine'
# gem 'puma'
gem 'puma'
gem 'byebug'
end
group :unix do
gem 'therubyracer' #, '0.11.0beta8'

View File

@ -1,257 +0,0 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.FindBandScreen = function(app) {
var logger = context.JK.logger;
var bands = {};
var bandList;
var instrument_logo_map = context.JK.getInstrumentIconMap24();
var did_show_band_page = false;
var page_num=1, page_count=0;
var helpBubble = context.JK.HelpBubbleHelper;
var $screen = $('#bands-screen');
var $results = $screen.find('#band-filter-results');
function loadBands(queryString) {
// squelch nulls and undefines
queryString = !!queryString ? queryString : "";
$.ajax({
type: "GET",
url: "/api/search.json?" + queryString,
success: afterLoadBands,
error: app.ajaxError
});
}
function search() {
did_show_band_page = true;
var queryString = 'srch_b=1&page='+page_num+'&';
// order by
var orderby = $('#band_order_by').val();
if (typeof orderby != 'undefined' && orderby.length > 0) {
queryString += "orderby=" + orderby + '&';
}
// genre filter
var genre = $('#band_genre').val();
if (typeof genre != 'undefined' && !(genre === '')) {
queryString += "genre=" + genre + '&';
}
// distance filter
var query_param = $('#band_query_distance').val();
if (query_param !== null && query_param.length > 0) {
var matches = query_param.match(/(\d+)/);
if (0 < matches.length) {
var distance = matches[0];
queryString += "distance=" + distance + '&';
}
}
loadBands(queryString);
}
function refreshDisplay() {
clearResults();
search();
}
function afterLoadBands(mList) {
// display the 'no bands' banner if appropriate
var $noBandsFound = $('#bands-none-found');
bandList = mList;
if(bandList.length == 0) {
$noBandsFound.show();
bands = [];
}
else {
$noBandsFound.hide();
bands = bandList['bands'];
if (!(typeof bands === 'undefined')) {
$('#band-filter-city').text(bandList['city']);
if (0 == page_count) {
page_count = bandList['page_count'];
}
renderBands();
}
}
}
function renderBands() {
var ii, len;
var mTemplate = $('#template-find-band-row').html();
var pTemplate = $('#template-band-player-info').html();
var aTemplate = $('#template-band-action-btns').html();
var eTemplate = $('#template-band-edit-btns').html();
var bVals, bb, renderings='';
var instr_logos, instr;
var players, playerVals, aPlayer, isMember;
for (ii=0, len=bands.length; ii < len; ii++) {
bb = bands[ii];
instr_logos = '';
players = '';
playerVals = {};
isMember = false;
for (var jj=0, ilen=bb['players'].length; jj<ilen; jj++) {
var toolTip = '';
aPlayer = bb['players'][jj];
var player_instrs = '';
var iter_pinstruments = aPlayer['instruments'].split(',');
for (var kk=0, klen=iter_pinstruments.length; kk<klen; kk++) {
var pinstr = iter_pinstruments[kk];
var toolTip = '';
if (pinstr in instrument_logo_map) {
instr = instrument_logo_map[pinstr].asset;
toolTip = pinstr;
}
player_instrs += '<img src="' + instr + '" title="' + toolTip + '"/>';
}
if (!isMember) {
isMember = aPlayer.user_id == context.JK.currentUserId;
}
playerVals = {
user_id: aPlayer.user_id,
player_name: aPlayer.name,
profile_url: '/client#/profile/' + aPlayer.user_id,
avatar_url: context.JK.resolveAvatarUrl(aPlayer.photo_url),
player_instruments: player_instrs
};
players += context.JK.fillTemplate(pTemplate, playerVals);
}
var actionVals, band_actions;
if (isMember) {
actionVals = {
profile_url: "/client#/bandProfile/" + bb.id,
band_edit_url: "/client#/band/setup/" + bb.id + '/step1',
band_member_url: "/client#/band/setup/" + bb.id + '/step2'
};
band_actions = context.JK.fillTemplate(eTemplate, actionVals);
} else {
actionVals = {
profile_url: "/client#/bandProfile/" + bb.id,
button_follow: bb['is_following'] ? '' : 'button-orange',
button_message: 'button-orange'
};
band_actions = context.JK.fillTemplate(aTemplate, actionVals);
}
var bgenres = '';
for (jj=0, ilen=bb['genres'].length; jj<ilen; jj++) {
bgenres += bb['genres'][jj]['description'] + '<br />';
}
bgenres += '<br />';
bVals = {
avatar_url: context.JK.resolveBandAvatarUrl(bb.photo_url),
profile_url: "/client#/bandProfile/" + bb.id,
band_name: bb.name,
band_location: bb.city + ', ' + bb.state,
genres: bgenres,
instruments: instr_logos,
biography: bb['biography'],
follow_count: bb['follow_count'],
recording_count: bb['recording_count'],
session_count: bb['session_count'],
band_id: bb['id'],
band_player_template: players,
band_action_template: band_actions
};
var $rendering = $(context.JK.fillTemplate(mTemplate, bVals))
var $offsetParent = $results.closest('.content');
var data = {entity_type: 'band'};
var options = {positions: ['top', 'bottom', 'right', 'left'], offsetParent: $offsetParent};
context.JK.helpBubble($('.follower-count', $rendering), 'follower-count', data, options);
context.JK.helpBubble($('.recording-count', $rendering), 'recording-count', data, options);
context.JK.helpBubble($('.session-count', $rendering), 'session-count', data, options);
$results.append($rendering);
}
$('.search-m-follow').on('click', followBand);
context.JK.bindHoverEvents();
}
function beforeShow(data) {
}
function afterShow(data) {
if (!did_show_band_page) {
refreshDisplay();
}
}
function clearResults() {
bands = {};
$('#band-filter-results').empty();
page_num = 1;
page_count = 0;
}
function followBand(evt) {
// if the band is already followed, remove the button-orange class, and prevent
// the link from working
if (0 == $(this).closest('.button-orange').size()) return false;
$(this).click(function(ee) {ee.preventDefault();});
evt.stopPropagation();
var newFollowing = {};
newFollowing.band_id = $(this).parent().data('band-id');
var url = "/api/users/" + context.JK.currentUserId + "/followings";
$.ajax({
type: "POST",
dataType: "json",
contentType: 'application/json',
url: url,
data: JSON.stringify(newFollowing),
processData: false,
success: function(response) {
// remove the orange look to indicate it's not selectable
// @FIXME -- this will need to be tweaked when we allow unfollowing
$('div[data-band-id='+newFollowing.band_id+'] .search-m-follow').removeClass('button-orange').addClass('button-grey');
},
error: app.ajaxError(arguments)
});
}
function events() {
$('#band_query_distance').change(refreshDisplay);
$('#band_genre').change(refreshDisplay);
$('#band_order_by').change(refreshDisplay);
$('#band-filter-results').closest('.content-body-scroller').bind('scroll', function() {
if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
if (page_num < page_count) {
page_num += 1;
search();
}
}
});
}
function initialize() {
var screenBindings = {
'beforeShow': beforeShow,
'afterShow': afterShow
};
app.bindScreen('bands', screenBindings);
events();
}
this.initialize = initialize;
this.renderBands = renderBands;
this.afterShow = afterShow;
this.clearResults = clearResults;
return this;
}
})(window,jQuery);

View File

@ -1798,6 +1798,19 @@
});
}
function getBandSearchFilter(query) {
var qarg = query === undefined ? '' : query;
return $.get("/api/search/bands.json?"+qarg);
}
function postBandSearchFilter(query) {
return $.ajax({
type: "POST",
url: "/api/search/bands.json",
data: query
});
}
function getMount(options) {
var id = getId(options);
return $.ajax({
@ -2046,6 +2059,8 @@
this.addRecordingTimeline = addRecordingTimeline;
this.getMusicianSearchFilter = getMusicianSearchFilter;
this.postMusicianSearchFilter = postMusicianSearchFilter;
this.getBandSearchFilter = getBandSearchFilter;
this.postBandSearchFilter = postBandSearchFilter;
this.playJamTrack = playJamTrack;
this.createSignupHint = createSignupHint;
this.createAlert = createAlert;

View File

@ -0,0 +1,858 @@
$ = jQuery
context = window
context.JK ||= {};
context.JK.BaseSearchFilter = class BaseSearchFilter
constructor: () ->
@rest = context.JK.Rest()
@logger = context.JK.logger
@searchFilter = null
@profileUtils = context.JK.ProfileUtils
@helpBubble = context.JK.HelpBubbleHelper
@searchResults = null
@isSearching = false
@pageNumber = 1
@instrument_logo_map = context.JK.getInstrumentIconMap24()
@searchType = ''
@searchTypeS = ''
@restGet = null
@restPost = null
@searchMeta = null
init: (app) =>
@app = app
@screenBindings = { 'afterShow': this.afterShow, 'afterHide': this.afterHide }
@app.bindScreen(@searchTypeS, @screenBindings)
@screen = $('#'+@searchTypeS+'-screen')
@resultsListContainer = @screen.find('#'+@searchType+'-search-filter-results-list')
@spinner = @screen.find('.paginate-wait')
this.registerResultsPagination()
afterShow: () =>
@screen.find('#'+@searchType+'-search-filter-results').show()
@screen.find('#'+@searchType+'-search-filter-builder').hide()
this.getUserFilterResults()
showBuilder: () =>
@screen.find('#'+@searchType+'-search-filter-results').hide()
@screen.find('#'+@searchType+'-search-filter-builder').show()
@resultsListContainer.empty()
afterHide: () =>
@resultsListContainer.empty()
searchMetaData: () =>
@searchMeta
filterData: () =>
@searchFilter.data_blob
renderSearchFilter: () =>
$.when(@restGet()).done (sFilter) =>
this.loadSearchFilter(sFilter)
loadSearchFilter: (sFilter) =>
_populateSelectWithKeys: (struct, selection, keys, element) =>
element.children().remove()
$.each keys, (idx, value) =>
label = struct[value]
blankOption = $ '<option value=""></option>'
blankOption.text label
blankOption.attr 'value', value
blankOption.attr 'selected', '' if value == selection
element.append(blankOption)
context.JK.dropdown(element)
_populateSelectIdentifier: (identifier) =>
elem = $ '#'+@searchType+'-search-filter-builder select[name='+identifier+']'
struct = this.searchMetaData()[identifier]['map']
keys = this.searchMetaData()[identifier]['keys']
this._populateSelectWithKeys(struct, this.filterData()[identifier], keys, elem)
_populateSelectWithInt: (sourceStruct, selection, element) =>
struct =
'-1': 'Any'
$.extend(struct, sourceStruct)
this._populateSelectWithKeys(struct, selection, Object.keys(struct).sort(), element)
_populateSortOrder: () =>
this._populateSelectIdentifier('sort_order')
_populateGigs: () =>
elem = $ '#'+@searchType+'-search-filter-builder select[name=concert_gigs]'
this._populateSelectWithInt(@profileUtils.gigMap, this.filterData().concert_gigs.toString(), elem)
_populateGenres: () =>
@screen.find('#search-filter-genres').empty()
@rest.getGenres().done (genres) =>
genreTemplate = @screen.find('#template-search-filter-setup-genres').html()
filterGenres = this.filterData().genres
$.each genres, (index, genre) =>
if 0 < filterGenres.length
genreMatch = $.grep(filterGenres, (n, i) ->
n == genre.id)
else
genreMatch = []
if genreMatch.length > 0 then selected = 'checked' else selected = ''
genreHtml = context.JK.fillTemplate(genreTemplate,
id: genre.id
description: genre.description
checked: selected)
@screen.find('#search-filter-genres').append genreHtml
_populateInstruments: () =>
@screen.find('#search-filter-instruments').empty()
@rest.getInstruments().done (instruments) =>
$.each instruments, (index, instrument) =>
instrumentTemplate = @screen.find('#template-search-filter-setup-instrument').html()
selected = ''
proficiency = '1'
if 0 < this.filterData().instruments.length
instMatch = $.grep(this.filterData().instruments, (inst, i) ->
yn = inst.instrument_id == instrument.id
proficiency = inst.proficiency_level if yn
yn)
selected = 'checked' if instMatch.length > 0
instrumentHtml = context.JK.fillTemplate(instrumentTemplate,
id: instrument.id
description: instrument.description
checked: selected)
@screen.find('#search-filter-instruments').append instrumentHtml
profsel = '#search-filter-instruments tr[data-instrument-id="'+instrument.id+'"] select'
jprofsel = @screen.find(profsel)
jprofsel.val(proficiency)
context.JK.dropdown(jprofsel)
return true
_builderSelectValue: (identifier) =>
elem = $ '#'+@searchType+'-search-filter-builder select[name='+identifier+']'
elem.val()
_builderSelectMultiValue: (identifier) =>
vals = []
elem = $ '#search-filter-'+identifier+' input[type=checkbox]:checked'
if 'instruments' == identifier
elem.each (idx) ->
row = $(this).parent().parent()
instrument =
instrument_id: row.data('instrument-id')
proficiency_level: row.find('select').val()
vals.push instrument
else
elem.each (idx) ->
if $(this).prop('checked')
vals.push $(this).val()
vals
willSearch: (reload) =>
return false if @isSearching
@isSearching = true
if reload
@pageNumber = 1
@screen.find('#'+@searchType+'-search-filter-spinner').show()
@resultsListContainer.empty()
@screen.find('#'+@searchType+'-search-filter-builder').hide()
@screen.find('#'+@searchType+'-search-filter-results').show()
true
didSearch: (response) =>
this.loadSearchFilter(response.filter_json)
@searchResults = response
@screen.find('#'+@searchType+'-search-filter-spinner').hide()
this.renderResultsPage()
@screen.find('.paginate-wait').hide()
@isSearching = false
resetFilter: () =>
if this.willSearch(true)
@restPost({ filter: 'reset' }).done(this.didSearch)
cancelFilter: () =>
this.resetFilter()
getUserFilterResults: () =>
if this.willSearch(true)
this.doRestGet()
doRestGet: (query) =>
query2 = 'results=true&'
unless (typeof query == "undefined")
query2 += query
@restGet(query2).done(this.didSearch)
performSearch: () =>
if this.willSearch(true)
$.each this.searchMetaData().filter_keys.single, (index, key) =>
this.filterData()[key] = this._builderSelectValue(key)
$.each this.searchMetaData().filter_keys.multi, (index, key) =>
this.filterData()[key] = this._builderSelectMultiValue(key)
@restPost({ filter: JSON.stringify(this.filterData()), page: @pageNumber }).done(this.didSearch)
renderResultsHeader: () =>
renderResultsPage: () =>
_formatLocation: (band) ->
if band.city and band.state
band.city + ', ' + band.state
else if band.city
band.city
else if band.regionname
band.regionname
else
'Location Unavailable'
friendRequestCallback: (user_id)=>
# TODO:
paginate: () =>
if @pageNumber < @searchResults.page_count && this.willSearch(false)
@screen.find('.paginate-wait').show()
@pageNumber += 1
@restPost({ filter: JSON.stringify(this.filterData()), page: @pageNumber }).done(this.didSearch)
return true
false
registerResultsPagination: () =>
_resultsListContainer = @resultsListContainer
_headerHeight = @screen.find('#'+@searchType+'-search-filter-results-header').height()
_paginator = this.paginate
@screen.find('.content-body-scroller').scroll ->
if _resultsListContainer.is(':visible')
jthis = $(this)
wintop = jthis.scrollTop()
winheight = jthis.innerHeight()
docheight = jthis[0].scrollHeight - _headerHeight
scrollTrigger = 0.98;
if ((wintop / (docheight - winheight)) >= scrollTrigger)
_paginator()
context.JK.MusicianSearchFilter = class MusicianSearchFilter extends BaseSearchFilter
constructor: () ->
super()
@searchType = 'musician'
@searchTypeS = @searchType+'s'
@restGet = @rest.getMusicianSearchFilter
@restPost = @rest.postMusicianSearchFilter
@searchMeta = gon.musician_search_meta
init: (app) =>
super(app)
@screen.find('#btn-'+@searchType+'-search-builder').on 'click', =>
this.showBuilder()
@screen.find('#btn-'+@searchType+'-search-reset').on 'click', =>
this.resetFilter()
renderSearchFilter: () =>
super()
loadSearchFilter: (sFilter) =>
super(sFilter)
@searchFilter = JSON.parse(sFilter)
args =
interests: this.filterData().interests
skill_level: this.filterData().skill_level
studio_sessions: this.filterData().studio_sessions
concert_gigs: this.filterData().concert_gigs
template = context.JK.fillTemplate(@screen.find('#template-musician-search-filter').html(), args)
content_root = @screen.find('#musician-search-filter-builder')
content_root.html template
@screen.find('#btn-perform-musician-search').on 'click', =>
this.performSearch()
@screen.find('#btn-musician-search-cancel').on 'click', =>
this.cancelFilter()
this._populateSkill()
this._populateStudio()
this._populateGigs()
this._populateInterests()
this._populateAges()
this._populateGenres()
this._populateInstruments()
this._populateSortOrder()
_populateSortOrder: () =>
this._populateSelectIdentifier('sort_order')
_populateInterests: () =>
this._populateSelectIdentifier('interests')
_populateStudio: () =>
elem = $ '#musician-search-filter-builder select[name=studio_sessions]'
this._populateSelectWithInt(@profileUtils.studioMap, this.filterData().studio_sessions.toString(), elem)
_populateAges: () =>
@screen.find('#search-filter-ages').empty()
ages_map = this.searchMetaData()['ages']['map']
$.each this.searchMetaData()['ages']['keys'], (index, key) =>
ageTemplate = @screen.find('#template-search-filter-setup-ages').html()
selected = ''
ageLabel = ages_map[key]
if 0 < this.filterData().ages.length
key_val = key.toString()
ageMatch = $.grep(this.filterData().ages, (n, i) ->
n == key_val)
selected = 'checked' if ageMatch.length > 0
ageHtml = context.JK.fillTemplate(ageTemplate,
id: key
description: ageLabel
checked: selected)
@screen.find('#search-filter-ages').append ageHtml
_populateGenres: () =>
super()
_populateSkill: () =>
elem = $ '#'+@searchType+'-search-filter-builder select[name=skill_level]'
this._populateSelectWithInt(@profileUtils.skillLevelMap, this.filterData().skill_level.toString(), elem)
_populateInstruments: () =>
super()
willSearch: (reload) =>
super(reload)
didSearch: (response) =>
super(response)
resetFilter: () =>
super()
cancelFilter: () =>
super()
getUserFilterResults: () =>
super()
performSearch: () =>
super()
renderResultsHeader: () =>
@screen.find('#'+@searchType+'-search-filter-description').html(@searchResults.description)
if @searchResults.is_blank_filter
@screen.find('#btn-'+@searchType+'-search-reset').hide()
else
@screen.find('#btn-'+@searchType+'-search-reset').show()
renderResultsPage: () =>
super()
this.renderResultsHeader() if @pageNumber == 1
musicians = @searchResults.musicians
len = musicians.length
if 0 == len
@screen.find('#musician-search-filter-results-list-blank').show()
@screen.find('#musician-search-filter-results-list-blank').html('No results found')
return
else
@screen.find('#musician-search-filter-results-list-blank').hide()
ii = 0
mTemplate = @screen.find('#template-search-musician-row').html()
aTemplate = @screen.find('#template-search-musician-action-btns').html()
mVals = undefined
musician = undefined
renderings = ''
instr_logos = undefined
follows = undefined
followVals = undefined
aFollow = undefined
myAudioLatency = @searchResults.my_audio_latency
while ii < len
musician = musicians[ii]
if context.JK.currentUserId == musician.id
ii++
continue
instr_logos = ''
jj = 0
ilen = musician['instruments'].length
while jj < ilen
instr_id = musician['instruments'][jj].instrument_id
if instr_img = @instrument_logo_map[instr_id]
instr_logos += '<img height="24" width="24" src="' + instr_img.asset + '" title="' + instr_id + '"/>'
jj++
actionVals =
profile_url: '/client#/profile/' + musician.id
friend_class: 'button-' + (if musician['is_friend'] then 'grey' else 'orange')
friend_caption: (if musician.is_friend then 'DIS' else '') + 'CONNECT'
follow_class: 'button-' + (if musician['is_following'] then 'grey' else 'orange')
follow_caption: (if musician.is_following then 'UN' else '') + 'FOLLOW'
message_class: 'button-orange'
message_caption: 'MESSAGE'
button_message: 'button-orange'
musician_actions = context.JK.fillTemplate(aTemplate, actionVals)
latencyBadge = context._.template($("#template-account-session-latency").html(), $.extend(sessionUtils.createLatency(musician), musician), variable: 'data')
mVals =
avatar_url: context.JK.resolveAvatarUrl(musician.photo_url)
profile_url: '/client#/profile/' + musician.id
musician_name: musician.name
musician_location: this._formatLocation(musician)
instruments: instr_logos
biography: musician['biography']
follow_count: musician['follow_count']
friend_count: musician['friend_count']
recording_count: musician['recording_count']
session_count: musician['session_count']
musician_id: musician['id']
musician_action_template: musician_actions
latency_badge: latencyBadge
musician_first_name: musician['first_name']
$rendering = $(context.JK.fillTemplate(mTemplate, mVals))
$offsetParent = @resultsListContainer.closest('.content')
data = entity_type: 'musician'
options =
positions: [
'top'
'bottom'
'right'
'left'
]
offsetParent: $offsetParent
scoreOptions = offsetParent: $offsetParent
context.JK.helpBubble($('.follower-count', $rendering), 'follower-count', data, options);
context.JK.helpBubble($('.friend-count', $rendering), 'friend-count', data, options);
context.JK.helpBubble($('.recording-count', $rendering), 'recording-count', data, options);
context.JK.helpBubble($('.session-count', $rendering), 'session-count', data, options);
@helpBubble.scoreBreakdown $('.latency', $rendering), false, musician['full_score'], myAudioLatency, musician['audio_latency'], musician['score'], scoreOptions
@resultsListContainer.append $rendering
$rendering.find('.biography').dotdotdot()
ii++
this._bindMessageMusician()
this._bindFriendMusician()
this._bindFollowMusician()
context.JK.bindHoverEvents()
return
_bindMessageMusician: () =>
objThis = this
@screen.find('.search-m-message').on 'click', (evt) ->
userId = $(this).parent().data('musician-id')
objThis.app.layout.showDialog 'text-message', d1: userId
_bindFriendMusician: () =>
objThis = this
@screen.find('.search-m-friend').on 'click', (evt) ->
# if the musician is already a friend, remove the button-orange class, and prevent the link from working
if 0 == $(this).closest('.button-orange').size()
return false
$(this).click (ee) ->
ee.preventDefault()
return
evt.stopPropagation()
uid = $(this).parent().data('musician-id')
objThis.rest.sendFriendRequest objThis.app, uid, this.friendRequestCallback
_bindFollowMusician: () =>
objThis = this
@screen.find('.search-m-follow').on 'click', (evt) ->
# if the musician is already followed, remove the button-orange class, and prevent the link from working
if 0 == $(this).closest('.button-orange').size()
return false
$(this).click (ee) ->
ee.preventDefault()
return
evt.stopPropagation()
newFollowing = {}
newFollowing.user_id = $(this).parent().data('musician-id')
url = '/api/users/' + context.JK.currentUserId + '/followings'
$.ajax
type: 'POST'
dataType: 'json'
contentType: 'application/json'
url: url
data: JSON.stringify(newFollowing)
processData: false
success: (response) ->
# remove the orange look to indicate it's not selectable
# @FIXME -- this will need to be tweaked when we allow unfollowing
objThis.screen.find('div[data-musician-id=' + newFollowing.user_id + '] .search-m-follow').removeClass('button-orange').addClass 'button-grey'
return
error: objThis.app.ajaxError
_formatLocation: (musician) ->
if musician.city and musician.state
musician.city + ', ' + musician.state
else if musician.city
musician.city
else if musician.regionname
musician.regionname
else
'Location Unavailable'
friendRequestCallback: (user_id)=>
# TODO:
paginate: () =>
super()
registerResultsPagination: () =>
super()
context.JK.BandSearchFilter = class BandSearchFilter extends BaseSearchFilter
constructor: () ->
super()
@searchType = 'band'
@searchTypeS = @searchType+'s'
@restGet = @rest.getBandSearchFilter
@restPost = @rest.postBandSearchFilter
@searchMeta = gon.band_search_meta
@searchSubType = 'to_join'
init: (app) =>
super(app)
@screen.find('#btn-'+@searchType+'-search-builder-to_join').on 'click', =>
this.showBuilderToJoin()
@screen.find('#btn-'+@searchType+'-search-builder-to_hire').on 'click', =>
this.showBuilderToHire()
@screen.find('#btn-'+@searchType+'-search-builder').on 'click', =>
this.showBuilderActive()
@screen.find('#btn-'+@searchType+'-search-reset').on 'click', =>
this.resetFilter()
@resultsList = @screen.find('#band-search-filter-results-list')
@screen.find('#band-search-filter-results-filtered').hide()
isToHire: () =>
'to_hire' == @searchSubType
showBuilderActive: () =>
if this.isToHire()
this.showBuilderToHire()
else
this.showBuilderToJoin()
showBuilderToJoin: () =>
@screen.find('.band-search-filter-builder-top-to_join').show()
@screen.find('.band-search-filter-builder-top-to_hire').hide()
@searchSubType = 'to_join'
this.showBuilder()
this._loadSearchFilter() if @searchFilter
# @screen.find('.band-search-filter-builder-top-to_join h2').html('search bands')
showBuilderToHire: () =>
@screen.find('.band-search-filter-builder-top-to_join').hide()
@screen.find('.band-search-filter-builder-top-to_hire').show()
@searchSubType = 'to_hire'
this.showBuilder()
this._loadSearchFilter() if @searchFilter
# @screen.find('.band-search-filter-builder-top-to_hire h2').html('search bands to hire')
searchMetaData: () =>
@searchMeta[@searchSubType]
renderSearchFilter: () =>
super()
_searchFilterArgsToJoin: () =>
args =
touring_option: this.filterData().touring_option
band_status: this.filterData().band_status
play_commitment: this.filterData().play_commitment
band_type: this.filterData().band_type
concert_gigs: this.filterData().concert_gigs
_searchFilterArgsToHire: () =>
if 0 < this.filterData().max_cost
has_max_cost = 'checked'
else
has_max_cost = ''
if 1==this.filterData().free_gigs
has_free_gigs = 'checked'
else
has_free_gigs = ''
args =
band_status: this.filterData().band_status
concert_gigs: this.filterData().concert_gigs
performance_samples: this.filterData().performance_samples
has_max_cost: has_max_cost
max_cost: this.filterData().max_cost
has_free_gigs: has_free_gigs
_populateSearchFilterToJoin: () =>
this._populateInstruments()
this._populateSkill()
this._populateGigs()
this._populatePlayCommit()
this._populateTourOption()
this._populateBandStatus()
_populateSearchFilterToHire: () =>
this._populateBandStatus()
this._populateGigs()
this._populatePerformSamples()
loadSearchFilter: (sFilter) =>
super(sFilter)
@searchFilter = JSON.parse(sFilter)
this._loadSearchFilter()
_loadSearchFilter: () =>
switch @searchSubType
when 'to_join' then args = this._searchFilterArgsToJoin()
when 'to_hire' then args = this._searchFilterArgsToHire()
template = context.JK.fillTemplate(@screen.find('#template-band-search-filter-'+@searchSubType).html(), args)
content_root = @screen.find('#band-search-filter-builder')
content_root.html template
@screen.find('#btn-perform-band-search').on 'click', =>
this.performSearch()
@screen.find('#btn-band-search-cancel').on 'click', =>
this.cancelFilter()
this._populateGenres()
this._populateSortOrder() if this.isToHire()
switch @searchSubType
when 'to_join' then this._populateSearchFilterToJoin()
when 'to_hire' then this._populateSearchFilterToHire()
_populateSkill: () =>
this._populateSelectIdentifier('band_type')
_populateBandStatus: () =>
this._populateSelectIdentifier('band_status')
_populatePlayCommit: () =>
this._populateSelectIdentifier('play_commitment')
_populateTourOption: () =>
this._populateSelectIdentifier('touring_option')
_populateSortOrder: () =>
this._populateSelectIdentifier('sort_order')
_populatePerformSamples: () =>
this._populateSelectIdentifier('performance_samples')
_populateGenres: () =>
super()
_populateInstruments: () =>
super()
willSearch: (reload) =>
super(reload)
didSearch: (response) =>
super(response)
resetFilter: () =>
if this.willSearch(true)
@screen.find('#band-search-filter-results-blank').show()
@screen.find('#band-search-filter-results-filtered').hide()
@screen.find('#band-search-filter-description').html('')
@restPost({ filter: 'reset', subtype: @searchSubType }).done(this.didSearch)
cancelFilter: () =>
super()
doRestGet: (query) =>
super('subtype='+@searchSubType)
performSearch: () =>
if this.willSearch(true)
filterPost = this.filterData()
$.each this.searchMetaData().filter_keys.single, (index, key) =>
filterPost[key] = this._builderSelectValue(key)
$.each this.searchMetaData().filter_keys.multi, (index, key) =>
filterPost[key] = this._builderSelectMultiValue(key)
if this.isToHire()
filterPost['max_cost'] = parseInt($('#max_cost_amount').val())
filterPost['free_gigs'] = if $('#free_gigs').prop('checked') then 1 else 0
postData = { subtype: @searchSubType, filter: JSON.stringify(filterPost), page: @pageNumber }
@restPost(postData).done(this.didSearch)
renderResultsHeader: () =>
if @searchResults.is_blank_filter
@screen.find('#band-search-filter-results-blank').show()
@screen.find('#band-search-filter-results-filtered').hide()
else
@screen.find('#band-search-filter-results-blank').hide()
@screen.find('#band-search-filter-results-filtered').show()
@screen.find('#band-search-filter-description').html(@searchResults.description)
renderResultsPage: () =>
super()
this.renderResultsHeader() if @pageNumber == 1
bands = @searchResults.bands
len = bands.length
if 0 == len
@screen.find('#band-search-filter-results-list').hide()
@screen.find('#band-search-filter-results-list-blank').show()
@screen.find('#band-search-filter-results-list-blank').html('No results found')
return
else
@screen.find('#band-search-filter-results-list-blank').hide()
@screen.find('#band-search-filter-results-list').show()
this.renderBands(bands)
renderBands: (bands) =>
toolTip = undefined
ii = undefined
len = undefined
mTemplate = $('#template-find-band-row').html()
pTemplate = $('#template-band-player-info').html()
aTemplate = $('#template-band-action-btns').html()
eTemplate = $('#template-band-edit-btns').html()
bVals = undefined
bb = undefined
renderings = ''
instr_logos = undefined
instr = undefined
players = undefined
playerVals = undefined
aPlayer = undefined
isMember = undefined
ii = 0
len = bands.length
while ii < len
bb = bands[ii]
instr_logos = ''
players = ''
playerVals = {}
isMember = false
jj = 0
ilen = bb['players'].length
while jj < ilen
toolTip = ''
aPlayer = bb['players'][jj]
player_instrs = ''
iter_pinstruments = aPlayer['instruments'].split(',')
kk = 0
klen = iter_pinstruments.length
while kk < klen
pinstr = iter_pinstruments[kk]
toolTip = ''
if pinstr of @instrument_logo_map
instr = @instrument_logo_map[pinstr].asset
toolTip = pinstr
player_instrs += '<img src="' + instr + '" title="' + toolTip + '"/>'
kk++
if !isMember
isMember = aPlayer.user_id == context.JK.currentUserId
playerVals =
user_id: aPlayer.user_id
player_name: aPlayer.name
profile_url: '/client#/profile/' + aPlayer.user_id
avatar_url: context.JK.resolveAvatarUrl(aPlayer.photo_url)
player_instruments: player_instrs
players += context.JK.fillTemplate(pTemplate, playerVals)
jj++
actionVals = undefined
band_actions = undefined
if isMember
actionVals =
profile_url: '/client#/bandProfile/' + bb.id
band_edit_url: '/client#/band/setup/' + bb.id + '/step1'
band_member_url: '/client#/band/setup/' + bb.id + '/step2'
band_actions = context.JK.fillTemplate(eTemplate, actionVals)
else
actionVals =
profile_url: '/client#/bandProfile/' + bb.id
button_follow: if bb['is_following'] then '' else 'button-orange'
button_message: 'button-orange'
band_actions = context.JK.fillTemplate(aTemplate, actionVals)
bgenres = ''
jj = 0
ilen = bb['genres'].length
while jj < ilen
bgenres += bb['genres'][jj]['description'] + '<br />'
jj++
bgenres += '<br />'
bVals =
avatar_url: context.JK.resolveBandAvatarUrl(bb.photo_url)
profile_url: '/client#/bandProfile/' + bb.id
band_name: bb.name
band_location: bb.city + ', ' + bb.state
genres: bgenres
instruments: instr_logos
biography: bb['biography']
follow_count: bb['follow_count']
recording_count: bb['recording_count']
session_count: bb['session_count']
band_id: bb['id']
band_player_template: players
band_action_template: band_actions
$rendering = $(context.JK.fillTemplate(mTemplate, bVals))
$offsetParent = @resultsList.closest('.content')
data = entity_type: 'band'
options =
positions: [
'top'
'bottom'
'right'
'left'
]
offsetParent: $offsetParent
context.JK.helpBubble $('.follower-count', $rendering), 'follower-count', data, options
context.JK.helpBubble $('.recording-count', $rendering), 'recording-count', data, options
context.JK.helpBubble $('.session-count', $rendering), 'session-count', data, options
@resultsList.append $rendering
ii++
this._bindFollowBand()
context.JK.bindHoverEvents()
return
_bindFollowBand: () =>
objThis = this
@screen.find('.search-m-follow').on 'click', (evt) ->
if 0 == $(this).closest('.button-orange').size()
return false
$(this).click (ee) ->
ee.preventDefault()
return
evt.stopPropagation()
newFollowing = {}
newFollowing.band_id = $(this).parent().data('band-id')
url = '/api/users/' + context.JK.currentUserId + '/followings'
$.ajax
type: 'POST'
dataType: 'json'
contentType: 'application/json'
url: url
data: JSON.stringify(newFollowing)
processData: false
success: (response) ->
$('div[data-band-id=' + newFollowing.band_id + '] .search-m-follow').removeClass('button-orange').addClass 'button-grey'
return
error: objThis.app.ajaxError(arguments)
_formatLocation: (band) ->
friendRequestCallback: (user_id)=>
# TODO:
paginate: () =>
super()
registerResultsPagination: () =>
super()
filterData: () =>
super()[@searchSubType]

View File

@ -1,412 +0,0 @@
$ = jQuery
context = window
context.JK ||= {};
context.JK.MusicianSearchFilter = class MusicianSearchFilter
constructor: () ->
@rest = context.JK.Rest()
@logger = context.JK.logger
@searchFilter = null
@profileUtils = context.JK.ProfileUtils
@helpBubble = context.JK.HelpBubbleHelper
@searchResults = null
@isSearching = false
@pageNumber = 1
@instrument_logo_map = context.JK.getInstrumentIconMap24()
@instrumentSelector = null
init: (app) =>
@app = app
@screenBindings = { 'afterShow': this.afterShow, 'afterHide': this.afterHide }
@app.bindScreen('musicians', @screenBindings)
@screen = $('#musicians-screen')
@resultsListContainer = @screen.find('#musician-search-filter-results-list')
@spinner = @screen.find('.paginate-wait')
@instrumentSelector = new context.JK.InstrumentSelector(JK.app)
@instrumentSelector.initialize(false, true)
this.registerResultsPagination()
@screen.find('#btn-musician-search-builder').on 'click', =>
this.showBuilder()
false
@screen.find('#btn-musician-search-reset').on 'click', =>
this.resetFilter()
false
afterShow: () =>
@screen.find('#musician-search-filter-results').show()
@screen.find('#musician-search-filter-builder').hide()
this.getUserFilterResults()
showBuilder: () =>
@screen.find('#musician-search-filter-results').hide()
@screen.find('#musician-search-filter-builder').show()
@resultsListContainer.empty()
afterHide: () =>
@resultsListContainer.empty()
renderSearchFilter: () =>
$.when(this.rest.getMusicianSearchFilter()).done (sFilter) =>
this.loadSearchFilter(sFilter)
loadSearchFilter: (sFilter) =>
@searchFilter = JSON.parse(sFilter)
args =
interests: @searchFilter.data_blob.interests
skill_level: @searchFilter.data_blob.skill_level
studio_sessions: @searchFilter.data_blob.studio_sessions
concert_gigs: @searchFilter.data_blob.concert_gigs
template = context.JK.fillTemplate(@screen.find('#template-musician-search-filter').html(), args)
content_root = @screen.find('#musician-search-filter-builder')
content_root.html template
@screen.find('#btn-perform-musician-search').on 'click', =>
this.performSearch()
false
@screen.find('#btn-musician-search-cancel').on 'click', =>
this.cancelFilter()
false
this._populateSkill()
this._populateStudio()
this._populateGigs()
this._populateInterests()
this._populateAges()
this._populateGenres()
this._populateInstruments()
this._populateSortOrder()
_populateSelectWithKeys: (struct, selection, keys, element) =>
element.children().remove()
$.each keys, (idx, value) =>
label = struct[value]
blankOption = $ '<option value=""></option>'
blankOption.text label
blankOption.attr 'value', value
element.append(blankOption)
element.val(selection)
context.JK.dropdown(element)
_populateSelectIdentifier: (identifier) =>
elem = $ '#musician-search-filter-builder select[name='+identifier+']'
struct = gon.musician_search_meta[identifier]['map']
keys = gon.musician_search_meta[identifier]['keys']
console.log("@searchFilter", @searchFilter, identifier)
this._populateSelectWithKeys(struct, @searchFilter.data_blob[identifier], keys, elem)
_populateSelectWithInt: (sourceStruct, selection, element) =>
struct =
'-1': 'Any'
$.extend(struct, sourceStruct)
this._populateSelectWithKeys(struct, selection, Object.keys(struct).sort(), element)
_populateSortOrder: () =>
this._populateSelectIdentifier('sort_order')
_populateInterests: () =>
this._populateSelectIdentifier('interests')
_populateStudio: () =>
elem = $ '#musician-search-filter-builder select[name=studio_sessions]'
this._populateSelectWithInt(@profileUtils.studioMap, @searchFilter.data_blob.studio_sessions.toString(), elem)
_populateGigs: () =>
elem = $ '#musician-search-filter-builder select[name=concert_gigs]'
this._populateSelectWithInt(@profileUtils.gigMap, @searchFilter.data_blob.concert_gigs.toString(), elem)
_populateSkill: () =>
elem = $ '#musician-search-filter-builder select[name=skill_level]'
this._populateSelectWithInt(@profileUtils.skillLevelMap, @searchFilter.data_blob.skill_level.toString(), elem)
_populateAges: () =>
@screen.find('#search-filter-ages').empty()
ages_map = gon.musician_search_meta['ages']['map']
$.each gon.musician_search_meta['ages']['keys'], (index, key) =>
ageTemplate = @screen.find('#template-search-filter-setup-ages').html()
ageLabel = ages_map[key]
if 0 < @searchFilter.data_blob.ages.length
key_val = key.toString()
ageMatch = $.grep(@searchFilter.data_blob.ages, (n, i) ->
n == key_val)
selected = 'checked' if ageMatch.length > 0
ageHtml = context._.template(ageTemplate,
{ id: key
description: ageLabel
checked: selected},
{variable: 'data'})
@screen.find('#search-filter-ages').append ageHtml
_populateGenres: () =>
@screen.find('#search-filter-genres').empty()
@rest.getGenres().done (genres) =>
genreTemplate = @screen.find('#template-search-filter-setup-genres').html()
$.each genres, (index, genre) =>
if 0 < @searchFilter.data_blob.genres.length
genreMatch = $.grep(@searchFilter.data_blob.genres, (n, i) ->
n == genre.id)
else
genreMatch = []
selected = 'checked' if genreMatch.length > 0
genreHtml = context._.template(genreTemplate,
{ id: genre.id
description: genre.description
checked: selected },
{ variable: 'data' })
@screen.find('#search-filter-genres').append genreHtml
_populateInstruments: () =>
# TODO hydrate user's selection from json_store
@instrumentSelector.render(@screen.find('.session-instrumentlist'), [])
@instrumentSelector.setSelectedInstruments(@searchFilter.data_blob.instruments)
_builderSelectValue: (identifier) =>
elem = $ '#musician-search-filter-builder select[name='+identifier+']'
elem.val()
_builderSelectMultiValue: (identifier) =>
vals = []
elem = $ '#search-filter-'+identifier+' input[type=checkbox]:checked'
if 'instruments' == identifier
vals = @instrumentSelector.getSelectedInstruments()
else
elem.each (idx) ->
vals.push $(this).val()
vals
willSearch: (reload) =>
return false if @isSearching
@isSearching = true
if reload
@pageNumber = 1
@screen.find('#musician-search-filter-spinner').show()
@resultsListContainer.empty()
@screen.find('#musician-search-filter-builder').hide()
@screen.find('#musician-search-filter-results').show()
true
didSearch: (response) =>
this.loadSearchFilter(response.filter_json)
@searchResults = response
@screen.find('#musician-search-filter-spinner').hide()
this.renderMusicians()
@screen.find('.paginate-wait').hide()
@isSearching = false
resetFilter: () =>
if this.willSearch(true)
@rest.postMusicianSearchFilter({ filter: 'reset' }).done(this.didSearch)
cancelFilter: () =>
this.resetFilter()
getUserFilterResults: () =>
if this.willSearch(true)
@rest.getMusicianSearchFilter('results=true').done(this.didSearch)
performSearch: () =>
if this.willSearch(true)
filter = {}
$.each gon.musician_search_meta.filter_keys.single, (index, key) =>
filter[key] = this._builderSelectValue(key)
$.each gon.musician_search_meta.filter_keys.multi, (index, key) =>
filter[key] = this._builderSelectMultiValue(key)
@rest.postMusicianSearchFilter({ filter: JSON.stringify(filter), page: @pageNumber }).done(this.didSearch)
renderResultsHeader: () =>
if @searchResults.is_blank_filter
@screen.find('#btn-musician-search-reset').hide()
@screen.find('.musician-search-text').text('Click search button to look for musicians with similar interests, skill levels, etc.')
else
@screen.find('#btn-musician-search-reset').show()
@screen.find('.musician-search-text').text(@searchResults.summary)
renderMusicians: () =>
this.renderResultsHeader() if @pageNumber == 1
musicians = @searchResults.musicians
len = musicians.length
if 0 == len
@screen.find('#musician-search-filter-results-list-blank').show()
@screen.find('#musician-search-filter-results-list-blank').html('No results found')
return
else
@screen.find('#musician-search-filter-results-list-blank').hide()
ii = 0
mTemplate = @screen.find('#template-search-musician-row').html()
aTemplate = @screen.find('#template-search-musician-action-btns').html()
mVals = undefined
musician = undefined
renderings = ''
instr_logos = undefined
follows = undefined
followVals = undefined
aFollow = undefined
myAudioLatency = @searchResults.my_audio_latency
while ii < len
musician = musicians[ii]
if context.JK.currentUserId == musician.id
ii++
continue
instr_logos = ''
jj = 0
ilen = musician['instruments'].length
while jj < ilen
instr_id = musician['instruments'][jj].instrument_id
if instr_img = @instrument_logo_map[instr_id]
instr_logos += '<img height="24" width="24" src="' + instr_img.asset + '" title="' + instr_id + '"/>'
jj++
actionVals =
profile_url: '/client#/profile/' + musician.id
friend_class: 'button-' + (if musician['is_friend'] then 'grey' else 'orange')
friend_caption: (if musician.is_friend then 'DIS' else '') + 'CONNECT'
follow_class: 'button-' + (if musician['is_following'] then 'grey' else 'orange')
follow_caption: (if musician.is_following then 'UN' else '') + 'FOLLOW'
message_class: 'button-orange'
message_caption: 'MESSAGE'
button_message: 'button-orange'
musician_actions = context.JK.fillTemplate(aTemplate, actionVals)
latencyBadge = context._.template($("#template-account-session-latency").html(), $.extend(sessionUtils.createLatency(musician), musician), variable: 'data')
mVals =
avatar_url: context.JK.resolveAvatarUrl(musician.photo_url)
profile_url: '/client#/profile/' + musician.id
musician_name: musician.name
musician_location: this._formatLocation(musician)
instruments: instr_logos
biography: musician['biography']
follow_count: musician['follow_count']
friend_count: musician['friend_count']
recording_count: musician['recording_count']
session_count: musician['session_count']
musician_id: musician['id']
musician_action_template: musician_actions
latency_badge: latencyBadge
musician_first_name: musician['first_name']
$rendering = $(context.JK.fillTemplate(mTemplate, mVals))
$offsetParent = @resultsListContainer.closest('.content')
data = entity_type: 'musician'
options =
positions: [
'top'
'bottom'
'right'
'left'
]
offsetParent: $offsetParent
scoreOptions = offsetParent: $offsetParent
context.JK.helpBubble($('.follower-count', $rendering), 'follower-count', data, options);
context.JK.helpBubble($('.friend-count', $rendering), 'friend-count', data, options);
context.JK.helpBubble($('.recording-count', $rendering), 'recording-count', data, options);
context.JK.helpBubble($('.session-count', $rendering), 'session-count', data, options);
@helpBubble.scoreBreakdown $('.latency', $rendering), false, musician['full_score'], myAudioLatency, musician['audio_latency'], musician['score'], scoreOptions
@resultsListContainer.append $rendering
$rendering.find('.biography').dotdotdot()
ii++
this._bindMessageMusician()
this._bindFriendMusician()
this._bindFollowMusician()
context.JK.bindHoverEvents()
return
_bindMessageMusician: () =>
objThis = this
@screen.find('.search-m-message').on 'click', (evt) ->
userId = $(this).parent().data('musician-id')
objThis.app.layout.showDialog 'text-message', d1: userId
return false
_bindFriendMusician: () =>
objThis = this
@screen.find('.search-m-friend').on 'click', (evt) =>
# if the musician is already a friend, remove the button-orange class, and prevent the link from working
$self = $(evt.target)
if 0 == $self.closest('.button-orange').size()
return false
logger.debug("evt.target", evt.target)
$self.click (ee) ->
ee.preventDefault()
return false
evt.stopPropagation()
uid = $self.parent().data('musician-id')
objThis.rest.sendFriendRequest objThis.app, uid, this.friendRequestCallback
@app.notify({text: 'A friend request has been sent.'})
return false
_bindFollowMusician: () =>
objThis = this
@screen.find('.search-m-follow').on 'click', (evt) ->
# if the musician is already followed, remove the button-orange class, and prevent the link from working
if 0 == $(this).closest('.button-orange').size()
return false
$(this).click (ee) ->
ee.preventDefault()
return false
evt.stopPropagation()
newFollowing = {}
newFollowing.user_id = $(this).parent().data('musician-id')
url = '/api/users/' + context.JK.currentUserId + '/followings'
$.ajax
type: 'POST'
dataType: 'json'
contentType: 'application/json'
url: url
data: JSON.stringify(newFollowing)
processData: false
success: (response) ->
# remove the orange look to indicate it's not selectable
# @FIXME -- this will need to be tweaked when we allow unfollowing
objThis.screen.find('div[data-musician-id=' + newFollowing.user_id + '] .search-m-follow').removeClass('button-orange').addClass 'button-grey'
return false
error: objThis.app.ajaxError
return false
_formatLocation: (musician) ->
if musician.city and musician.state
musician.city + ', ' + musician.state
else if musician.city
musician.city
else if musician.regionname
musician.regionname
else
'Location Unavailable'
friendRequestCallback: (user_id)=>
# TODO:
paginate: () =>
if @pageNumber < @searchResults.page_count && this.willSearch(false)
@screen.find('.paginate-wait').show()
@pageNumber += 1
@rest.postMusicianSearchFilter({ filter: JSON.stringify(@searchFilter.data_blob), page: @pageNumber }).done(this.didSearch)
return true
false
registerResultsPagination: () =>
_resultsListContainer = @resultsListContainer
_headerHeight = @screen.find('#musician-search-filter-results-header').height()
_paginator = this.paginate
@screen.find('.content-body-scroller').scroll ->
if _resultsListContainer.is(':visible')
jthis = $(this)
wintop = jthis.scrollTop()
winheight = jthis.innerHeight()
docheight = jthis[0].scrollHeight - _headerHeight
scrollTrigger = 0.98;
if ((wintop / (docheight - winheight)) >= scrollTrigger)
_paginator()

View File

@ -32,6 +32,7 @@ MIX_MODES = context.JK.MIX_MODES
componentClasses = classNames({
"session-track" : true
"metronome" : true
"in-jam-track" : @props.location == 'jam-track'
"no-mixer" : @props.mode == MIX_MODES.MASTER # show it as disabled if in master mode
"in-jam-track" : @props.location == 'jam-track'
})

View File

@ -32,6 +32,8 @@ context.JK.WebcamViewer = class WebcamViewer
this.loadResolutions()
this.selectResolution()
@initialScan = true
# protect against non-video clients pointed at video-enabled server from getting into a session
if @client.SessStopVideoSharing
@client.SessStopVideoSharing()
#client.SessSetInsetPosition(5)
#client.SessSetInsetSize(1)
@ -82,14 +84,16 @@ context.JK.WebcamViewer = class WebcamViewer
selectedDeviceName:() =>
webcamName="None Configured"
webcam = @client.FTUECurrentSelectedVideoDevice()
# protect against non-video clients pointed at video-enabled server from getting into a session
webcam = if @client.FTUECurrentSelectedVideoDevice? then @client.FTUECurrentSelectedVideoDevice() else null
if (webcam? && Object.keys(webcam).length>0)
webcamName = _.values(webcam)[0]
webcamName
loadWebCams:() =>
devices = @client.FTUEGetVideoCaptureDeviceNames()
# protect against non-video clients pointed at video-enabled server from getting into a session
devices = if @client.FTUEGetVideoCaptureDeviceNames? then @client.FTUEGetVideoCaptureDeviceNames() else []
selectedDevice = this.selectedDeviceName()
selectControl = @webcamSelect
context._.each devices, (device) ->
@ -107,7 +111,8 @@ context.JK.WebcamViewer = class WebcamViewer
@root.find('.no-webcam-msg').addClass 'hidden'
loadResolutions:() =>
resolutions = @client.FTUEGetAvailableEncodeVideoResolutions()
# protect against non-video clients pointed at video-enabled server from getting into a session
resolutions = if @client.FTUEGetAvailableEncodeVideoResolutions? then @client.FTUEGetAvailableEncodeVideoResolutions() else {}
selectControl = @resolutionSelect
@logger.debug 'FOUND THESE RESOLUTIONS', resolutions, selectControl
context._.each resolutions, (value, key, obj) ->

View File

@ -1,6 +1,6 @@
@import "client/common.css.scss";
#band-setup, #band-profile {
#band-setup, #band-profile, #bands-filter-to_hire, #bands-filter-to_join {
font-family: Raleway, Arial, Helvetica, verdana, arial, sans-serif;
.band-field {
input[type="text"], select, textarea {
@ -495,10 +495,178 @@
label {
font-size: 1.05em;
}
}
label.strong-label {
font-weight: bold;
font-size: 1.1em;
#bands-screen {
.col-left {
float: left;
width: 50%;
margin-left: auto;
margin-right: auto;
}
.col-right {
float: right;
width: 50%;
margin-left: auto;
margin-right: auto;
}
#band-search-filter-spinner {
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 40px;
}
#band-search-filter-results-list {
.profile-band-list-result {
padding-top: 5px;
}
}
.builder-section {
padding: 10px;
margin-bottom: 20px;
}
.builder-sort-order {
.text-label {
float: right;
margin-right: 10px;
padding-top: 5px;
}
.sort-order-selector {
float: right;
.easydropdown-wrapper {
width: 140px;
}
}
}
#band-search-filter-builder {
margin-left: 5px;
}
.band-search-filter-builder-bottom {
text-align: right;
#btn-perform-band-search {
width: 120px;
margin-left: 20px;
}
}
tr:nth-child(even) td {
padding-bottom: 0;
}
#band-search-filter-results-header {
padding: 10px 10px 10px 10px;
}
#band-search-filter-description {
padding: 5px 5px 5px 5px;
display: inline;
margin-left: auto;
margin-right: auto;
}
#btn-band-search-builder {
float: left;
margin-right: 20px;
}
#btn-band-search-reset {
float: right;
margin-left: 20px;
}
#bands-filter-to_hire {
width: 100%;
.builder-selector {
margin-top: 10px;
}
#max_cost_amount {
width: 60px;
}
.col-left {
float: left;
width: 45%;
margin-left: auto;
margin-right: auto;
padding-right: 20px;
}
.col-right {
float: right;
width: 45%;
margin-left: auto;
margin-right: auto;
.easydropdown-wrapper {
width: 100%;
}
}
}
#bands-filter-to_join {
width: 100%;
.builder-selector {
margin-top: 10px;
}
.lhs {
float: left;
width: 40%;
.easydropdown-wrapper {
width: 100%;
}
}
.rhs {
float: right;
width: 40%;
.easydropdown-wrapper {
width: 100%;
}
}
.col-left {
float: left;
width: 30%;
margin-left: auto;
margin-right: auto;
padding-right: 20px;
.easydropdown-wrapper {
width: 100%;
}
}
.col-right {
float: right;
width: 66%;
margin-left: auto;
margin-right: auto;
}
.search-filter-setup-genres {
}
.band-setup-genres {
.easydropdown-wrapper {
width: 100%;
}
}
.builder-section {
padding: 10px;
margin-bottom: 20px;
}
}
}

View File

@ -254,15 +254,15 @@
.col-left {
@include border_box_sizing;
float: left;
width: 33%;
width: 45%;
margin-left: auto;
margin-right: auto;
padding-right: 20px;
}
.col-right {
float: right;
width: 67%;
@include border_box_sizing;
width: 45%;
margin-left: auto;
margin-right: auto;
@ -306,6 +306,14 @@
}
.builder-ages {
width: 100%;
height: 100%;
background-color:#c5c5c5;
border:none;
-webkit-box-shadow: inset 2px 2px 3px 0px #888;
box-shadow: inset 2px 2px 3px 0px #888;
color:#000;
overflow:auto;
font-size:12px;
}
.builder-instruments {
width: 100%;
@ -321,6 +329,21 @@
}
}
.musician-setup-genres {
width:100%;
height:200px;
background-color:#c5c5c5;
border:none;
-webkit-box-shadow: inset 2px 2px 3px 0px #888;
box-shadow: inset 2px 2px 3px 0px #888;
color:#000;
overflow:auto;
font-size:12px;
.easydropdown-wrapper {
width: 100%;
}
}
}
.filter-element {

View File

@ -175,6 +175,17 @@
.track-controls {
margin-left:0;
}
&.in-jam-track {
min-height:56px;
.track-buttons {
margin-top:2px;
}
table.vu {
margin-top: 10px;
}
}
}
&.recorded-track, &.jam-track, &.recorded-category, &.jam-track-category {

View File

@ -482,7 +482,7 @@ class ApiMusicSessionsController < ApiController
comment.save
if comment.errors.any?
render :json => { :message => "Unexpected error occurred" }, :status => 500
render :json => { :errors => comment.errors }, :status => 422
return
else
render :json => {}, :status => 201
@ -508,7 +508,7 @@ class ApiMusicSessionsController < ApiController
comment.save
if comment.errors.any?
render :json => { :message => "Unexpected error occurred" }, :status => 500
render :json => { :errors => comment.errors }, :status => 422
return
else
music_session = MusicSession.find(params[:id])

View File

@ -155,7 +155,7 @@ class ApiRecordingsController < ApiController
comment.save
if comment.errors.any?
render :json => { :message => "Unexpected error occurred" }, :status => 500
render :json => { :errors => comment.errors }, :status => 422
return
else
render :json => {}, :status => 201
@ -178,7 +178,7 @@ class ApiRecordingsController < ApiController
liker.save
if liker.errors.any?
render :json => { :message => "Unexpected error occurred" }, :status => 500
render :json => { :errors => liker.errors }, :status => 422
return
else
render :json => {}, :status => 201

View File

@ -7,7 +7,7 @@ class ApiSearchController < ApiController
def index
if 1 == params[Search::PARAM_MUSICIAN].to_i || 1 == params[Search::PARAM_BAND].to_i
query = params.clone
query = parasobj.clone
query[:remote_ip] = request.remote_ip
if 1 == query[Search::PARAM_MUSICIAN].to_i
@search = Search.musician_filter(query, current_user)
@ -32,13 +32,35 @@ class ApiSearchController < ApiController
end
elsif request.post?
ms = MusicianSearch.user_search_filter(current_user)
sobj = MusicianSearch.user_search_filter(current_user)
filter = params[:filter]
if filter == 'reset'
@search = ms.reset_search_results
@search = sobj.reset_search_results
else
json = JSON.parse(filter, :create_additions => false)
@search = ms.search_results_page(json, [params[:page].to_i, 1].max)
@search = sobj.search_results_page(json, [params[:page].to_i, 1].max)
end
respond_with @search, responder: ApiResponder, status: 201, template: 'api_search/index'
end
end
def bands
if request.get?
if params[:results]
@search = BandSearch.user_search_filter(current_user).search_results_page(params[:subtype])
respond_with @search, responder: ApiResponder, status: 201, template: 'api_search/index'
else
render :json => BandSearch.search_filter_json(current_user, params[:subtype]), :status => 200
end
elsif request.post?
sobj = BandSearch.user_search_filter(current_user)
filter = params[:filter]
if filter == 'reset'
@search = sobj.reset_search_results(params[:subtype])
else
json = JSON.parse(filter, :create_additions => false)
@search = sobj.search_results_page(params[:subtype], json, [params[:page].to_i, 1].max)
end
respond_with @search, responder: ApiResponder, status: 201, template: 'api_search/index'
end

View File

@ -796,7 +796,7 @@ class ApiUsersController < ApiController
play.save
if play.errors.any?
render :json => { :message => "Unexpected error occurred" }, :status => 500
render :json => { :errors => play.errors }, :status => 422
else
render :json => {}, :status => 201
end

View File

@ -69,7 +69,12 @@ class SpikesController < ApplicationController
end
def musician_search_filter
# gon.musician_search_meta = MusicianSearch::SEARCH_FILTER_META
# gon.musician_search_meta = MusicianSearch.search_filter_meta
render :layout => 'web'
end
def band_search_filter
gon.band_search_meta = BandSearch.search_filter_meta
render :layout => 'web'
end

View File

@ -67,7 +67,8 @@ module ClientHelper
gon.ftue_network_test_duration = Rails.application.config.ftue_network_test_duration
gon.ftue_network_test_max_clients = Rails.application.config.ftue_network_test_max_clients
gon.ftue_maximum_gear_latency = Rails.application.config.ftue_maximum_gear_latency
gon.musician_search_meta = MusicianSearch::SEARCH_FILTER_META
gon.musician_search_meta = MusicianSearch.search_filter_meta
gon.band_search_meta = BandSearch.search_filter_meta
# is this the native client or browser?
@nativeClient = is_native_client?

View File

@ -3,7 +3,7 @@ object @jam_track
attributes :id, :name, :description, :recording_type, :original_artist, :songwriter, :publisher, :sales_region, :price, :version, :duration
node :genres do |item|
[item.genre.description] # XXX: need to return single genre; not array
item.genres.select(:description).map(&:description)
end
node :added_cart do |item|

View File

@ -1,9 +1,9 @@
object @jam_track
attributes :id, :name, :description, :initial_play_silence, :original_artist, :version, :genre
attributes :id, :name, :description, :initial_play_silence, :original_artist, :version
node :genre do |jam_track|
jam_track.genre.present? ? jam_track.genre.id : nil
node :genres do |item|
item.genres.select(:description).map(&:description)
end
node :jmep do |jam_track|

View File

@ -1,8 +1,6 @@
object @search
node :search_type do |ss| ss.search_type end
if @search.is_a?(MusicianSearch)
if @search.is_a?(BaseSearch)
node :page_count do |foo|
@search.page_count
@ -12,18 +10,19 @@ if @search.is_a?(MusicianSearch)
current_user.last_jam_audio_latency.round if current_user.last_jam_audio_latency
end
node :is_blank_filter do |foo|
@search.is_blank?
end
node :filter_json do |foo|
@search.to_json
end
node :summary do |foo|
if @search.is_a? MusicianSearch
node :description do |foo|
@search.description
end
node :is_blank_filter do |foo|
@search.is_blank?
end
child(:results => :musicians) {
attributes :id, :first_name, :last_name, :name, :city, :state, :country, :online, :musician, :photo_url, :biography, :regionname, :score, :full_score
@ -53,8 +52,8 @@ if @search.is_a?(MusicianSearch)
node :name do |uu| uu.name end
end
node :follow_count do |musician| @search.follow_count(musician) end
node :friend_count do |musician| @search.friend_count(musician) end
node :follow_count do |musician| @search.follow_count(musician) end
node :recording_count do |musician| @search.record_count(musician) end
node :session_count do |musician| @search.session_count(musician) end
@ -62,8 +61,49 @@ if @search.is_a?(MusicianSearch)
last_jam_audio_latency(musician)
end
}
elsif @search.is_a?(BandSearch)
node :is_blank_filter do |foo|
@search.is_blank?(params[:subtype])
end
node :description do |foo|
@search.description(params[:subtype])
end
child(:results => :bands) {
attributes :id, :name, :city, :state, :country, :photo_url, :biography, :logo_url, :website
node :is_following do |band|
@search.is_follower?(band)
end
node :biography do |band|
band.biography.nil? ? "" : band.biography
end
child :genres => :genres do
attributes :genre_id, :description
end
child :users => :players do |pl|
node :user_id do |uu| uu.id end
node :photo_url do |uu| uu.photo_url end
node :name do |uu| uu.name end
node :instruments do |uu| uu.instruments.map(&:id).join(',') end
end
node :follow_count do |band| @search.follow_count(band) end
node :recording_count do |band| @search.record_count(band) end
node :session_count do |band| @search.session_count(band) end
}
end
else
node :search_type do |ss| ss.search_type end
if @search.session_invite_search?
child(:results => :suggestions) {
node :value do |uu| uu.name end

View File

@ -0,0 +1,216 @@
.content-body-scroller
div#band-search-filter-builder.content-wrapper
div#band-search-filter-results.content-wrapper
div#band-search-filter-results-header
#band-search-filter-results-blank
a.button-orange#btn-band-search-builder-to_join href="#" SEARCH FOR BAND TO JOIN
|&nbsp;-&nbsp;or&nbsp;-&nbsp;
a.button-orange#btn-band-search-builder-to_hire href="#" SEARCH FOR BAND TO HIRE
#band-search-filter-results-filtered
a.button-orange#btn-band-search-builder href="#" SEARCH
a.button-grey#btn-band-search-reset href="#" RESET
div#band-search-filter-description
div.clearall
div#band-search-filter-spinner.spinner-large
div#band-search-filter-results-wrapper
div#band-search-filter-results-list-blank
div.content-wrapper#band-search-filter-results-list
div.paginate-wait
Fetching more results...
div.spinner-small
script#template-band-search-filter-to_join type="text/template"
#bands-filter-to_join
.band-search-filter-builder-top.builder-section
.col-left
h2 search bands
/ .col-right.builder-sort-order
/ .sort-order-selector
/ select.easydropdown name="sort_order"
/ option selected="selected" value="{sort_order}" {sort_order}
/ .text-label Sort Results By:
.clearall
.band-search-filter-builder-middle1.builder-section
.col-left
.field
label for="search-filter-genres" Genres:
.search-filter-setup-genres.band-setup-genres
table#search-filter-genres cellpadding="10" cellspacing="6" width="100%"
.col-right
.field
label for="search-filter-instruments"
| Looking for New Members with These Skills:
.search-filter-setup-instruments.band-setup-genres.builder-instruments
table#search-filter-instruments cellpadding="10" cellspacing="6" width="100%"
.clearall
.band-search-filter-builder-middle2.builder-section
.col-left
.field.builder-selector
label Type:
select.easydropdown name="band_type"
option selected="selected" value="{band_type}" {band_type}
.field.builder-selector
label Play Commitment:
select.easydropdown name="play_commitment"
option selected="selected" value="{play_commitment}" {play_commitment}
.col-right
.field.builder-selector
.lhs
label Status:
select.easydropdown name="band_status"
option selected="selected" value="{band_status}" {band_status}
.field.builder-selector
.rhs
label Concert Gigs Played:
select.easydropdown name="concert_gigs"
option selected="selected" value="{concert_gigs}" {concert_gigs}
.clearall
.field.builder-selector
.lhs
label Touring Option:
select.easydropdown name="touring_option"
option selected="selected" value="{touring_option}" {touring_option}
.clearall
.clearall
.band-search-filter-builder-bottom.builder-section.builder-action-buttons
.col-right
a#btn-band-search-cancel.builder-button.button-grey href="#" CANCEL
a#btn-perform-band-search.builder-button.button-orange href="#" SEARCH
script#template-band-search-filter-to_hire type="text/template"
#bands-filter-to_hire
.band-search-filter-builder-top-to_hire.builder-section
.col-left
h2 search bands to hire
.col-right.builder-sort-order
.sort-order-selector
select.easydropdown name="sort_order"
option selected="selected" value="{sort_order}" {sort_order}
.text-label Sort Results By:
.clearall
.band-search-filter-builder-middle1.builder-section
.col-left
.field
label for="search-filter-genres" Genres:
.search-filter-setup-genres.band-setup-genres
table#search-filter-genres cellpadding="10" cellspacing="6" width="100%"
.col-right
.field.builder-selector
label Status:
select.easydropdown name="band_status"
option selected="selected" value="{band_status}" {band_status}
.field.builder-selector
label Concert Gigs Played:
select.easydropdown name="concert_gigs"
option selected="selected" value="{concert_gigs}" {concert_gigs}
.field.builder-selector
label Performance Sample Available:
select.easydropdown name="performance_samples"
option selected="selected" value="{performance_samples}" {performance_samples}
.clearall
.band-search-filter-builder-middle2.builder-section
.field.builder-selector
<input type="checkbox" id="has_max_cost" {has_max_cost} >&nbsp;Find bands to play a paid gig at a cost not to exceed&nbsp;&nbsp;$
input type="text" id="max_cost_amount" name="max_cost" value="{max_cost}"
.field.builder-selector
<input type="checkbox" id="free_gigs" name="free_gigs" {has_free_gigs} >&nbsp;Find bands that will play free gigs
.clearall
.clearall
.band-search-filter-builder-bottom.builder-section.builder-action-buttons
.col-right
a#btn-band-search-cancel.builder-button.button-grey href="#" CANCEL
a#btn-perform-band-search.builder-button.button-orange href="#" SEARCH
script#template-search-filter-setup-instrument type="text/template"
tr data-instrument-id="{id}"
td <input type="checkbox" {checked} />{description}
td align="right" width="50%"
select.proficiency_selector name="proficiency"
option value="1" Beginner
option value="2" Intermediate
option value="3" Expert
script#template-search-filter-setup-genres type="text/template"
tr
td <input value="{id}" {checked} type="checkbox" />{description}
script#template-find-band-row type="text/template"
.profile-band-list-result.band-list-result
.f11
.left style="width:63px;margin-top:-12px;"
.avatar-small
img src="{avatar_url}" /
.right.mband-players style="width:265px; margin-top:-4px;"
table.musicians cellpadding="0" cellspacing="5"
| {band_player_template}
div style="margin-left: 63px; margin-right: 275px;margin-top: 12px;"
.first-row data-hint="top-row"
.lcol.left
.result-name
| {band_name}
.result-location
| {band_location}
br
#result_genres.nowrap.mt10
| {genres}
.whitespace
.biography
| {biography}
.clearleft
.button-row
.lcol.stats.left
span.follower-count
| {follow_count}
img src="../assets/content/icon_followers.png" width="22" height="12" align="absmiddle" style="margin-right:4px;"
span.recording-count
| {recording_count}
img src="../assets/content/icon_recordings.png" width="12" height="13" align="absmiddle" style="margin-right:4px;"
span.session-count
| {session_count}
img src="../assets/content/icon_session_tiny.png" width="12" height="12" align="absmiddle"
.clearall
.result-list-button-wrapper data-band-id="{band_id}"
| {band_action_template}
.clearall
script#template-band-action-btns type="text/template"
a.button-orange.smallbutton href="{profile_url}" PROFILE
a.smallbutton.search-m-follow href="#" class="{button_follow}" FOLLOW
script#template-band-edit-btns type="text/template"
a.button-orange.smallbutton href="{profile_url}" PROFILE
a.button-orange.smallbutton href="{band_edit_url}" EDIT BAND
a.button-orange.smallbutton href="{band_member_url}" INVITE
script#template-band-player-info type="text/template"
tr
td
a.avatar-tiny href="{profile_url}" user-id="{user_id}" hoveraction="musician"
img src="{avatar_url}"
td style="padding: 0 4px;width:88px;"
a user-id="{user_id}" hoveraction="musician" href="{profile_url}"
strong
| {player_name}
td.instruments
| {player_instruments}

View File

@ -1,86 +0,0 @@
<!-- Band Screen -->
<%= content_tag(:div, :layout => 'screen', 'layout-id' => 'bands', :class => "screen secondary", :id => "bands-screen") do -%>
<%= content_tag(:div, :class => :content) do -%>
<%= content_tag(:div, :class => 'content-head') do -%>
<%= content_tag(:div, image_tag("content/icon_bands.png", {:height => 19, :width => 19}), :class => 'content-icon') %>
<%= content_tag(:h1, 'bands') %>
<%= render "screen_navigation" %>
<% end -%>
<%= content_tag(:div, :class => 'content-body') do -%>
<%= form_tag('', {:id => 'find-band-form', :class => 'inner-content'}) do -%>
<%= render(:partial => "web_filter", :locals => {:search_type => Search::PARAM_BAND}) %>
<%= content_tag(:div, :class => 'filter-body') do %>
<%= content_tag(:div, :class => 'content-body-scroller') do -%>
<%= content_tag(:div, content_tag(:div, '', :id => 'band-filter-results', :class => 'filter-results'), :class => 'content-wrapper') %>
<% end -%>
<% end -%>
<% end -%>
<% end -%>
<% end -%>
<% end -%>
<!-- Session Row Template -->
<script type="text/template" id="template-find-band-row"><!-- -->
<div class="profile-band-list-result band-list-result">
<div class="f11">
<div class="left" style="width:63px;margin-top:-12px;">
<!-- avatar -->
<div class="avatar-small"><img src="{avatar_url}" /></div>
</div>
<div class="right mband-players" style="width:265px; margin-top:-4px;">
<table class="musicians" cellpadding="0" cellspacing="5">{band_player_template}</table>
</div>
<div class="" style="margin-left: 63px; margin-right: 275px;margin-top: 12px;">
<div class="first-row" data-hint="top-row">
<div class="lcol left">
<!-- name & location -->
<div class="result-name">{band_name}</div>
<div class="result-location">{band_location}</div>
<br />
<div id="result_genres" class="nowrap mt10">{genres}</div>
</div>
<div class="whitespace">
<div class="biography">{biography}</div>
</div>
<div class="clearleft"></div>
</div>
<div class="button-row">
<div class="lcol stats left">
<span class="follower-count">{follow_count} <img src="../assets/content/icon_followers.png" width="22" height="12" align="absmiddle" style="margin-right:4px;"/></span>
<span class="recording-count">{recording_count} <img src="../assets/content/icon_recordings.png" width="12" height="13" align="absmiddle" style="margin-right:4px;"/></span>
<span class="session-count">{session_count} <img src="../assets/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" /></span>
</div>
<div class="clearall"></div>
<div class="result-list-button-wrapper" data-band-id={band_id}>
{band_action_template}
</div>
<div class="clearall"></div>
</div>
</div>
</div>
</div>
</script>
<script type="text/template" id="template-band-action-btns">
<a href="{profile_url}" class="button-orange smallbutton">PROFILE</a>
<a href="#" class="{button_follow} smallbutton search-m-follow">FOLLOW</a>
</script>
<script type="text/template" id="template-band-edit-btns">
<a href="{profile_url}" class="button-orange smallbutton">PROFILE</a>
<a href="{band_edit_url}" class="button-orange smallbutton">EDIT BAND</a>
<a href="{band_member_url}" class="button-orange smallbutton">INVITE</a>
</script>
<script type="text/template" id="template-band-player-info">
<tr>
<td>
<a href="{profile_url}" user-id="{user_id}" hoveraction="musician" class="avatar-tiny"><img src="{avatar_url}" /></a>
</td>
<td style="padding: 0 4px;width:88px;">
<a user-id="{user_id}" hoveraction="musician" href="{profile_url}"><strong>{player_name}</strong></a>
</td>
<td class="instruments">{player_instruments}</td>
</tr>
</script>

View File

@ -0,0 +1,140 @@
#bands-screen.screen.secondary layout="screen" layout-id="bands"
.content
.content-head
.content-icon
img alt="Icon_bands" height="19" src="/assets/content/icon_bands.png" width="19" /
h1 bands
= render "screen_navigation"
.content-body
= render "clients/band_search_filter"
script#template-band-search-filter-to_join type="text/template"
#bands-filter-to_join
#band-search-filter-builder-top.builder-section
.col-left
h2 search bands
.col-right.builder-sort-order
.text-label Sort Results By:
select.easydropdown name="sort_order"
option selected="selected" value="{sort_order}" {sort_order}
.clearall
#band-search-filter-builder-middle1.builder-section
.col-left
.field
label for="search-filter-genres" Genres:
.search-filter-setup-genres.band-setup-genres
table#search-filter-genres cellpadding="10" cellspacing="6" width="100%"
.col-right
.field
label for="search-filter-instruments"
| Instruments &amp; Skill Level:
.search-filter-setup-instruments.band-setup-genres.builder-instruments
table#search-filter-instruments cellpadding="10" cellspacing="6" width="100%"
.clearall
#band-search-filter-builder-middle2.builder-section
.col-left
.field.builder-selector
label Type:
select.easydropdown name="band_type"
option selected="selected" value="{band_type}" {band_type}
.field.builder-selector
label Play Commitment:
select.easydropdown name="play_commit"
option selected="selected" value="{play_commit}" {play_commit}
.col-right
.field.builder-selector
label Status:
select.easydropdown name="skill_level"
option selected="selected" value="{skill_level}" {skill_level}
.field.builder-selector
label Concert Gigs Played:
select.easydropdown name="concert_gigs"
option selected="selected" value="{concert_gigs}" {concert_gigs}
.field.builder-selector
label Touring Option:
select.easydropdown name="tour_option"
option selected="selected" value="{tour_option}" {tour_option}
.clearall
.clearall
#band-search-filter-builder-bottom.builder-section.builder-action-buttons
.col-right
a#btn-perform-band-search.builder-button.button-orange href="#" SEARCH
a#btn-band-search-cancel.builder-button.button-grey href="#" CANCEL
/! Session Row Template
script#template-search-band-row type="text/template"
.profile-band-list-result.band-list-result
.f11
.left style="width:63px;margin-top:-12px;"
/! avatar
.avatar-small
img src="{avatar_url}" /
.right.mband-players style="width:265px; margin-top:-4px;"
table.musicians cellpadding="0" cellspacing="5"
| {band_player_template}
div style="margin-left: 63px; margin-right: 275px;margin-top: 12px;"
.first-row data-hint="top-row"
.lcol.left
/! name and location
.result-name
| {band_name}
.result-location
| {band_location}
br /
#result_genres.nowrap.mt10
| {genres}
.whitespace
.biography
| {biography}
.clearleft
.button-row
.lcol.stats.left
span.follower-count
| {follow_count}
img src="../assets/content/icon_followers.png" width="22" height="12" align="absmiddle" style="margin-right:4px;" /
span.recording-count
| {recording_count}
img src="../assets/content/icon_recordings.png" width="12" height="13" align="absmiddle" style="margin-right:4px;" /
span.session-count
| {session_count}
img src="../assets/content/icon_session_tiny.png" width="12" height="12" align="absmiddle" /
.clearall
.result-list-button-wrapper data-band-id="{band_id}"
| {band_action_template}
.clearall
script#template-search-band-action-btns type="text/template"
a.button-orange smallbutton href="{profile_url}"
PROFILE
a class="{button_follow} smallbutton search-m-follow" href="#"
FOLLOW
script#template-search-band-edit-btns type="text/template"
a href="{profile_url}" class="button-orange smallbutton"
PROFILE
a href="{band_edit_url}" class="button-orange smallbutton"
EDIT BAND
a href="{band_member_url}" class="button-orange smallbutton"
INVITE
script#template-search-band-player-info type="text/template"
tr
td
a.avatar-tiny href="{profile_url}" user-id="{user_id}" hoveraction="musician"
img src="{avatar_url}" /
td style="padding: 0 4px;width:88px"
a user-id="{user_id}" hoveraction="musician" href="{profile_url}"
strong
| {player_name}
td.instruments
| {player_instruments}

View File

@ -4,7 +4,6 @@
div#musician-search-filter-results.content-wrapper
div#musician-search-filter-results-header
a#btn-musician-search-builder.button-orange href="#" SEARCH
span.musician-search-text Click search button to look for musicians with similar interests, skill levels, etc.
a#btn-musician-search-reset.button-grey href="#" RESET
div#musician-search-filter-description
div.clearall
@ -30,43 +29,43 @@ script#template-musician-search-filter type="text/template"
#musician-search-filter-builder-middle.builder-section
.col-left
.field
label for="search-filter-genres" Genres
.search-filter-setup-genres.band-setup-genres
#search-filter-genres
label for="search-filter-genres" Genres:
.search-filter-setup-genres.musician-setup-genres
table#search-filter-genres cellpadding="10" cellspacing="6" width="100%"
.field.builder-selector
label Interests
label Interests:
select.easydropdown name="interests"
option selected="selected" value="{interests}" {interests}
.field.builder-selector
label Studio Sessions Played
label Studio Sessions Played:
select.easydropdown name="studio_sessions"
option selected="selected" value="{studio_sessions}" {studio_sessions}
.col-right
.field
label for="search-filter-instruments"
| Instruments &amp; Skill Level
.search-filter-setup-instruments.band-setup-genres.builder-instruments.session-instrumentlist
| Instruments &amp; Skill Level:
.search-filter-setup-instruments.musician-setup-genres.builder-instruments
table#search-filter-instruments cellpadding="10" cellspacing="6" width="100%"
.col-left
.field.builder-selector
label Status
label Status:
select.easydropdown name="skill_level"
option selected="selected" value="{skill_level}" {skill_label}
.field.builder-selector
label Concert Gigs Played
label Concert Gigs Played:
select.easydropdown name="concert_gigs"
option selected="selected" value="{concert_gigs}" {concert_gigs}
.col-right
.field.builder-selector
label for="search-filter-ages" Ages
.search-filter-setup-ages.band-setup-genres.builder-ages
#search-filter-ages
label for="search-filter-ages" Ages:
.search-filter-setup-ages.builder-ages
table#search-filter-ages cellpadding="10" cellspacing="6" width="100%"
.clearall
.clearall
@ -86,24 +85,12 @@ script#template-search-filter-setup-instrument type="text/template"
option value="3" Expert
script#template-search-filter-setup-genres type="text/template"
.genre-option
| {% if(data.checked) { %}
input value="{{data.id}}" checked="checked" type="checkbox"
| {% } else { %}
input value="{{data.id}}" type="checkbox"
| {% } %}
label
| {{data.description}}
tr
td <input value="{id}" {checked} type="checkbox" />{description}
script#template-search-filter-setup-ages type="text/template"
.age-option
| {% if(data.checked) { %}
input value="{{data.id}}" checked="checked" type="checkbox"
| {% } else { %}
input value="{{data.id}}" type="checkbox"
| {% } %}
label
| {{data.description}}
tr
td <input value="{id}" {checked} type="checkbox" />{description}
/! Session Row Template
script#template-search-musician-row type="text/template"

View File

@ -314,21 +314,17 @@
// var findMusicianScreen = new JK.FindMusicianScreen(JK.app);
//findMusicianScreen.initialize(JK.TextMessageDialogInstance);
var findBandScreen = new JK.BandSearchFilter();
findBandScreen.init(JK.app);
var redeemSignUpScreen = new JK.RedeemSignUpScreen(JK.app);
redeemSignUpScreen.initialize();
var redeemCompleteScreen = new JK.RedeemCompleteScreen(JK.app);
redeemCompleteScreen.initialize();
var findBandScreen = new JK.FindBandScreen(JK.app);
findBandScreen.initialize();
//var sessionScreen = new JK.SessionScreen(JK.app);
//sessionScreen.initialize(localRecordingsDialog, recordingFinishedDialog, JK.FriendSelectorDialogInstance);
AppActions.appInit.trigger(JK.app)
var addNewGearDialog = new JK.AddNewGearDialog(JK.app);
addNewGearDialog.initialize();

View File

@ -0,0 +1,15 @@
= javascript_include_tag "profile_utils"
= javascript_include_tag "member_search_filter"
= stylesheet_link_tag "client/band"
#bands-screen
= render "clients/band_search_filter"
javascript:
var initialized = false;
$(document).on('JAMKAZAM_READY', function(e, data) {
setTimeout(function() {
window.band_search_filter = new JK.BandSearchFilter();
band_search_filter.init(data.app);
band_search_filter.afterShow();
}, 1)
});

View File

@ -1,15 +1,15 @@
= javascript_include_tag "profile_utils"
= javascript_include_tag "musician_search_filter"
= javascript_include_tag "member_search_filter"
= stylesheet_link_tag "client/musician"
#musician_search_spike
= render "clients/musician_search_filter"
#musicians-screen
= render "clients/musician_search_filter"
javascript:
var initialized = false;
$(document).on('JAMKAZAM_READY', function(e, data) {
setTimeout(function() {
window.musician_search_filter = new JK.MusicianSearchFilter();
musician_search_filter.init();
musician_search_filter.init(data.app);
musician_search_filter.afterShow();
}, 1)
});

View File

@ -118,6 +118,7 @@ SampleApp::Application.routes.draw do
match '/site_validate', to: 'spikes#site_validate'
match '/recording_source', to: 'spikes#recording_source'
match '/musician_search_filter', to: 'spikes#musician_search_filter'
match '/band_search_filter', to: 'spikes#band_search_filter'
# junk pages
match '/help', to: 'static_pages#help'
@ -485,6 +486,7 @@ SampleApp::Application.routes.draw do
# search
match '/search' => 'api_search#index', :via => :get
match '/search/musicians' => 'api_search#musicians', :via => [:get, :post]
match '/search/bands' => 'api_search#bands', :via => [:get, :post]
# join requests
match '/join_requests/:id' => 'api_join_requests#show', :via => :get, :as => 'api_join_request_detail'

View File

@ -5,24 +5,11 @@ class MaxMindManager < BaseManager
end
def self.countries
Country.get_all.map do |c|
country = Carmen::Country.coded(c.countrycode)
{
countrycode: c.countrycode,
countryname: country.name
}
end
Country.get_all.map { |c| {countrycode: c.countrycode, countryname: c.countryname} }
end
def self.regions(country)
Region.get_all(country).map do |r|
country = Carmen::Country.coded(r.countrycode)
region = country.subregions.coded(r.region)
{
region: r.region,
name: region.name
}
end
Region.get_all(country).map { |r| { region: r.region, name: r.regionname } }
end
def self.cities(country, region)

View File

@ -4,15 +4,49 @@ namespace :jam_tracks do
JamTrackImporter.dry_run
end
task tency_dry_run: :environment do |task, args|
JamTrackImporter.storage_format = 'Tency'
JamTrackImporter.dry_run
end
task tency_create_masters: :environment do |task, args|
JamTrackImporter.storage_format = 'Tency'
JamTrackImporter.create_masters
end
task tency_create_master: :environment do |task, args|
JamTrackImporter.storage_format = 'Tency'
path = ENV['TRACK_PATH']
if !path
puts "TRACK_PATH must be set to something like audio/AC DC/Back in Black or mapped/50 Cent - In Da Club - 12401"
exit(1)
end
JamTrackImporter.create_master(path)
end
task tency_delta: :environment do |task, args|
JamTrackImporter.storage_format = 'Tency'
JamTrackImporter.tency_delta
end
task sync: :environment do |task, args|
path = ENV['TRACK_PATH']
if !path
puts "TRACK_PATH must be set to something like AD DC/Back in Black"
puts "TRACK_PATH must be set to something like audio/AC DC/Back in Black or mapped/50 Cent - In Da Club - 12401"
exit(1)
end
JamTrackImporter.synchronize_from_meta("audio/#{path}/meta.yml", skip_audio_upload:false)
if path.start_with?('mapped')
JamTrackImporter.storage_format = 'Tency'
end
JamTrackImporter.synchronize_from_meta("#{path}/meta.yml", skip_audio_upload:false)
end
task resync_audio: :environment do |task, args|
@ -26,11 +60,26 @@ namespace :jam_tracks do
JamTrackImporter.synchronize_from_meta("audio/#{path}/meta.yml", resync_audio:true, skip_audio_upload:false)
end
task tency_genre_dump: :environment do |task, args|
JamTrackImporter.storage_format = 'Tency'
JamTrackImporter.genre_dump
end
task sync_tency: :environment do |task, args|
JamTrackImporter.storage_format = 'Tency'
JamTrackImporter.synchronize_all(skip_audio_upload:false)
end
task onboarding_exceptions: :environment do |task, args|
JamTrackImporter.onboarding_exceptions
end
task sync_all: :environment do |task, args|
JamTrackImporter.synchronize_all(skip_audio_upload:false)
end
task sync_all_dev: :environment do |task, args|
JamTrackImporter.storage_format = 'default'
JamTrackImporter.synchronize_all(skip_audio_upload:true)
end
@ -91,4 +140,10 @@ namespace :jam_tracks do
task download_masters: :environment do |task, arg|
JamTrackImporter.download_masters
end
task tency: :environment do |task, arg|
mapper = TencyStemMapping.new
mapper.correlate
end
end

View File

@ -139,7 +139,7 @@ end
def make_band_members
Band.find_each do |bb|
User.order('RANDOM()').limit(4).each do |uu|
BandMusician.create!({:user_id => uu.id, :band_id => bb.id})
BandMusician.create({:user_id => uu.id, :band_id => bb.id})
end
end
end

View File

@ -128,6 +128,7 @@ describe ApiJamTracksController do
# of this process is checked in other tests:
@ogg_path = File.join('spec', 'files', 'on.ogg')
@jam_track = FactoryGirl.create(:jam_track) #jam_track_track.jam_track
@jam_track.reload
jam_track_track = @jam_track.jam_track_tracks.first
# 48 kHz:

View File

@ -743,7 +743,7 @@ FactoryGirl.define do
make_track true
end
genre JamRuby::Genre.first
genres [JamRuby::Genre.first]
association :licensor, factory: :jam_track_licensor
after(:create) do |jam_track, evaluator|

View File

@ -5,8 +5,8 @@ describe "JamTrack Shopping", :js => true, :type => :feature, :capybara_feature
let(:user) { FactoryGirl.create(:user, has_redeemable_jamtrack: false) }
let(:jt_us) { FactoryGirl.create(:jam_track, :name=>'jt_us', sales_region: 'Worldwide', make_track: true, original_artist: "foobar") }
let(:jt_ww) { FactoryGirl.create(:jam_track, :name=>'jt_ww', sales_region: 'Worldwide', make_track: true, original_artist: "barfoo") }
let(:jt_rock) { FactoryGirl.create(:jam_track, :name=>'jt_rock', genre: JamRuby::Genre.find('rock'), make_track: true, original_artist: "badfood") }
let(:jt_blues) { FactoryGirl.create(:jam_track, :name=>'jt_blues', genre: JamRuby::Genre.find('blues'), make_track: true, original_artist: "foodbart") }
let(:jt_rock) { FactoryGirl.create(:jam_track, :name=>'jt_rock', genres: [JamRuby::Genre.find('rock')], make_track: true, original_artist: "badfood") }
let(:jt_blues) { FactoryGirl.create(:jam_track, :name=>'jt_blues', genres: [JamRuby::Genre.find('blues')], make_track: true, original_artist: "foodbart") }
before(:all) do
Capybara.javascript_driver = :poltergeist