diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb
index 6b2d70a20..7d7ffd4e6 100755
--- a/ruby/lib/jam_ruby.rb
+++ b/ruby/lib/jam_ruby.rb
@@ -239,7 +239,9 @@ 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/band_search"
require "jam_ruby/import/tency_stem_mapping"
include Jampb
diff --git a/ruby/lib/jam_ruby/models/band.rb b/ruby/lib/jam_ruby/models/band.rb
index c3d4b471d..2ba858c6e 100644
--- a/ruby/lib/jam_ruby/models/band.rb
+++ b/ruby/lib/jam_ruby/models/band.rb
@@ -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
diff --git a/ruby/lib/jam_ruby/models/band_search.rb b/ruby/lib/jam_ruby/models/band_search.rb
new file mode 100644
index 000000000..976250935
--- /dev/null
+++ b/ruby/lib/jam_ruby/models/band_search.rb
@@ -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
diff --git a/ruby/lib/jam_ruby/models/base_search.rb b/ruby/lib/jam_ruby/models/base_search.rb
new file mode 100644
index 000000000..f08c46a7d
--- /dev/null
+++ b/ruby/lib/jam_ruby/models/base_search.rb
@@ -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
diff --git a/ruby/lib/jam_ruby/models/musician_search.rb b/ruby/lib/jam_ruby/models/musician_search.rb
index 9b780c53f..f11e36e20 100644
--- a/ruby/lib/jam_ruby/models/musician_search.rb
+++ b/ruby/lib/jam_ruby/models/musician_search.rb
@@ -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,81 +48,28 @@ 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 => [],
- 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
-
- 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 },
- 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
+ def self.json_schema
+ return @@jschema if @@jschema
+ @@jschema = BaseSearch.json_schema.merge({
+ KEY_INTERESTS => INTEREST_VALS[0],
+ KEY_STUDIOS => STUDIO_COUNTS[0].to_s,
+ KEY_AGES => [],
+ KEY_SKILL => self::SKILL_VALS[0].to_s,
+ })
end
- def self.search_filter_json(user)
- self.user_search_filter(user).json
+ 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 }
+ })
end
- def self.create_search(user)
- ms = self.new
- ms.user = user
- ms.data_blob = JSON_SCHEMA
- ms.save!
- ms
+ def self.search_target_class
+ User
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
- end
-
def _ages(rel)
unless (vals = json[KEY_AGES]).blank?
return rel if vals.detect { |vv| ANY_VAL_INT == vv }
@@ -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
- 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
+ def search_includes(rel)
+ rel.includes([:instruments, :followings, :friends])
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
diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb
index 217c73339..691f03a30 100644
--- a/ruby/lib/jam_ruby/models/user.rb
+++ b/ruby/lib/jam_ruby/models/user.rb
@@ -172,6 +172,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
diff --git a/ruby/spec/jam_ruby/models/band_filter_search_spec.rb b/ruby/spec/jam_ruby/models/band_filter_search_spec.rb
index 1805a0873..bc2c0c206 100644
--- a/ruby/spec/jam_ruby/models/band_filter_search_spec.rb
+++ b/ruby/spec/jam_ruby/models/band_filter_search_spec.rb
@@ -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
+
+ describe "creates search obj" do
+
+ before(:all) do
+ User.delete_all
+ end
+
+ it "associates to user" do
+ expect(search.user).to eq(searcher)
+ searcher.reload
+ expect(searcher.band_search).to eq(search)
+ 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
+
+ it "loads all bands by default" do
+ search.search_results_page
+ expect(search.results.count).to eq(Band.count)
+ end
+
+ 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
- context 'default filter settings' do
+ describe "generates description" 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)
+ 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
- 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 })
+ it "renders no description for blank" do
+ search.search_results_page
+ expect(search.description).to eq('')
+ end
+
+ 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
+
+ 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 "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 "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
+
+ 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
+
+ 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 'to_hire' do
- rbands = @bands.reverse
- results.results.each_with_index do |uu, idx|
- expect(uu.id).to eq(@bands.reverse[idx].id)
- end
- end
+ let!(:filter) { to_hire }
- 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 "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
- users.each_with_index do |u, index|
- if index != 0
- f1 = Follow.new
- f1.user = u
- f1.followable = @band3
- f1.save
- end
+ 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
- f1 = Follow.new
- f1.user = users.first
- f1.followable = @band2
- f1.save
+ 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)
- # establish sorting order
- # @band4.followers.concat(users[1..-1])
- # @band3.followers.concat(users[1..3])
- # @band2.followers.concat(users[0])
- @bands.map(&:reload)
+ filter[BandSearch::KEY_PERF_SAMPLES] = BandSearch::VAL_NO
+ search.search_results_page(BandSearch::TO_HIRE, filter)
+ expect(search.results.count).to eq(Band.count)
- 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
+ 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
- # @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
- 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
- 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
- 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)
- 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
- 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)
- 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)
-
- results = Search.band_filter({ :orderby => 'playing' })
- expect(results.results.count).to be 1
- expect(results.results.first.id).to eq(@band3.id)
-
- # 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)
- 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
- 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
- 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
- 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
diff --git a/web/Gemfile b/web/Gemfile
index 35c68d23c..924910f1b 100644
--- a/web/Gemfile
+++ b/web/Gemfile
@@ -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'
diff --git a/web/app/assets/javascripts/findBand.js b/web/app/assets/javascripts/findBand.js
deleted file mode 100644
index e7c30f315..000000000
--- a/web/app/assets/javascripts/findBand.js
+++ /dev/null
@@ -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';
- }
- 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';
- }
-
- bgenres += '
';
-
- 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);
\ No newline at end of file
diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js
index 0f1445290..1a6c2c59a 100644
--- a/web/app/assets/javascripts/jam_rest.js
+++ b/web/app/assets/javascripts/jam_rest.js
@@ -1746,6 +1746,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({
@@ -1991,6 +2004,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;
diff --git a/web/app/assets/javascripts/member_search_filter.js.coffee b/web/app/assets/javascripts/member_search_filter.js.coffee
new file mode 100644
index 000000000..357806ebc
--- /dev/null
+++ b/web/app/assets/javascripts/member_search_filter.js.coffee
@@ -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 = $ ''
+ 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 += '
'
+ 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 += '
'
+ 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'] + '
'
+ jj++
+ bgenres += '
'
+ 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]
+
diff --git a/web/app/assets/javascripts/musician_search_filter.js.coffee b/web/app/assets/javascripts/musician_search_filter.js.coffee
deleted file mode 100644
index 9586374b8..000000000
--- a/web/app/assets/javascripts/musician_search_filter.js.coffee
+++ /dev/null
@@ -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 = $ ''
- 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 += '
'
- 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()
diff --git a/web/app/assets/stylesheets/client/band.css.scss b/web/app/assets/stylesheets/client/band.css.scss
index e3b33b7a3..2cbdc6b26 100644
--- a/web/app/assets/stylesheets/client/band.css.scss
+++ b/web/app/assets/stylesheets/client/band.css.scss
@@ -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;
+ }
+
+ }
}
\ No newline at end of file
diff --git a/web/app/assets/stylesheets/client/musician.css.scss b/web/app/assets/stylesheets/client/musician.css.scss
index 7fcd81f54..69f9b82fe 100644
--- a/web/app/assets/stylesheets/client/musician.css.scss
+++ b/web/app/assets/stylesheets/client/musician.css.scss
@@ -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 {
diff --git a/web/app/controllers/api_search_controller.rb b/web/app/controllers/api_search_controller.rb
index 0663265ff..54e3ab5b0 100644
--- a/web/app/controllers/api_search_controller.rb
+++ b/web/app/controllers/api_search_controller.rb
@@ -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
diff --git a/web/app/controllers/spikes_controller.rb b/web/app/controllers/spikes_controller.rb
index 0edce85ce..069a2d757 100644
--- a/web/app/controllers/spikes_controller.rb
+++ b/web/app/controllers/spikes_controller.rb
@@ -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
diff --git a/web/app/helpers/client_helper.rb b/web/app/helpers/client_helper.rb
index c4977e130..f6febfb1d 100644
--- a/web/app/helpers/client_helper.rb
+++ b/web/app/helpers/client_helper.rb
@@ -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?
diff --git a/web/app/views/api_search/index.rabl b/web/app/views/api_search/index.rabl
index 4296eb164..12e7fbf53 100644
--- a/web/app/views/api_search/index.rabl
+++ b/web/app/views/api_search/index.rabl
@@ -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|
- @search.description
- end
+ 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
diff --git a/web/app/views/clients/_band_search_filter.html.slim b/web/app/views/clients/_band_search_filter.html.slim
new file mode 100644
index 000000000..58340e5d1
--- /dev/null
+++ b/web/app/views/clients/_band_search_filter.html.slim
@@ -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
+ | - or -
+ 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
+ Find bands to play a paid gig at a cost not to exceed $
+ input type="text" id="max_cost_amount" name="max_cost" value="{max_cost}"
+
+ .field.builder-selector
+ 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 {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 {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}
diff --git a/web/app/views/clients/_bands.html.erb b/web/app/views/clients/_bands.html.erb
deleted file mode 100644
index ae7bdbf5c..000000000
--- a/web/app/views/clients/_bands.html.erb
+++ /dev/null
@@ -1,86 +0,0 @@
-
-<%= 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 -%>
-
-
-
-
-
-
-
-
-
-
diff --git a/web/app/views/clients/_bands.html.slim b/web/app/views/clients/_bands.html.slim
new file mode 100644
index 000000000..c472fbe39
--- /dev/null
+++ b/web/app/views/clients/_bands.html.slim
@@ -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 & 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}
+
diff --git a/web/app/views/clients/_musician_search_filter.html.slim b/web/app/views/clients/_musician_search_filter.html.slim
index bbd9a949a..b454bee83 100644
--- a/web/app/views/clients/_musician_search_filter.html.slim
+++ b/web/app/views/clients/_musician_search_filter.html.slim
@@ -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
@@ -22,51 +21,51 @@ script#template-musician-search-filter type="text/template"
.col-left
h2 search musicians
.col-right.builder-sort-order
- .text-label Sort Results By:
- select.easydropdown name="sort_order"
- option selected="selected" value="{sort_order}" {sort_order}
+ .text-label Sort Results By:
+ select.easydropdown name="sort_order"
+ option selected="selected" value="{sort_order}" {sort_order}
.clearall
#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 & Skill Level
- .search-filter-setup-instruments.band-setup-genres.builder-instruments.session-instrumentlist
+ | Instruments & 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
@@ -85,25 +84,13 @@ script#template-search-filter-setup-instrument type="text/template"
option value="2" Intermediate
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}}
+script#template-search-filter-setup-genres type="text/template"
+ tr
+ td {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}}
+script#template-search-filter-setup-ages type="text/template"
+ tr
+ td {description}
/! Session Row Template
script#template-search-musician-row type="text/template"
diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb
index 5002f53ea..31eabb4bd 100644
--- a/web/app/views/clients/index.html.erb
+++ b/web/app/views/clients/index.html.erb
@@ -310,21 +310,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();
diff --git a/web/app/views/spikes/band_search_filter.html.slim b/web/app/views/spikes/band_search_filter.html.slim
new file mode 100644
index 000000000..a98478205
--- /dev/null
+++ b/web/app/views/spikes/band_search_filter.html.slim
@@ -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)
+ });
diff --git a/web/app/views/spikes/musician_search_filter.html.slim b/web/app/views/spikes/musician_search_filter.html.slim
index 777cf2636..d6ae1f1da 100644
--- a/web/app/views/spikes/musician_search_filter.html.slim
+++ b/web/app/views/spikes/musician_search_filter.html.slim
@@ -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)
});
diff --git a/web/config/routes.rb b/web/config/routes.rb
index 2281f6937..22807e2f8 100644
--- a/web/config/routes.rb
+++ b/web/config/routes.rb
@@ -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'
@@ -478,6 +479,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'
diff --git a/web/lib/tasks/sample_data.rake b/web/lib/tasks/sample_data.rake
index 8ba62eb5a..58d25782b 100644
--- a/web/lib/tasks/sample_data.rake
+++ b/web/lib/tasks/sample_data.rake
@@ -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