jam-cloud/ruby/lib/jam_ruby/models/band_search.rb

431 lines
13 KiB
Ruby
Raw Normal View History

module JamRuby
2015-05-07 17:11:24 +00:00
class BandSearch < BaseSearch
cattr_accessor :jschema, :search_meta
attr_accessor :user_counters
#serialize :data_blob, JSON
2015-05-26 08:43:11 +00:00
KEY_BAND_SEARCH_TYPE = 'band_search_type'
2015-05-27 08:20:46 +00:00
KEY_BAND_TYPE = 'band_type'
2015-05-27 04:10:05 +00:00
KEY_BAND_STATUS = 'band_status'
2015-05-27 02:58:04 +00:00
KEY_PLAY_COMMIT = 'play_commitment'
2015-05-27 04:10:05 +00:00
KEY_TOUR_OPTION = 'touring_option'
KEY_PERF_SAMPLES = 'performance_samples'
KEY_HIRE_MAX_COST = 'max_cost'
KEY_HIRE_FREE = 'free_gigs'
2015-05-26 08:43:11 +00:00
TO_JOIN = 'to_join'
TO_HIRE = 'to_hire'
BAND_SEARCH_TYPE_VALS = [TO_JOIN, TO_HIRE]
BAND_SEARCH_TYPES = {
2015-05-26 08:43:11 +00:00
TO_JOIN => 'search bands',
TO_HIRE => 'search bands to hire',
}
2015-05-27 08:20:46 +00:00
BAND_TYPE_VAL_STRS = [ANY_VAL_STR, 'amateur', 'professional']
2015-06-04 07:38:16 +00:00
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,
2015-06-04 07:38:16 +00:00
}
2015-05-27 04:10:05 +00:00
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)',
}
2015-05-27 04:10:05 +00:00
BAND_STATUS_VALS = [ANY_VAL_STR,
GenrePlayer::VIRTUAL_BAND,
GenrePlayer::TRADITIONAL_BAND,
]
2015-05-27 08:20:46 +00:00
BAND_STATUS = {
2015-05-27 04:10:05 +00:00
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',
2015-05-26 06:39:43 +00:00
'3',
2015-05-27 02:58:04 +00:00
'4',
]
PLAY_COMMITS = {
PLAY_COMMIT_VALS[0] => 'Any',
PLAY_COMMIT_VALS[1] => 'Infrequent',
2015-05-27 02:58:04 +00:00
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,
2015-05-27 02:58:04 +00:00
VAL_YES,
VAL_NO,
]
TOUR_OPTIONS = {
TOUR_OPTION_VALS[0] => 'Any',
2015-05-27 02:58:04 +00:00
TOUR_OPTION_VALS[1] => VAL_YES,
TOUR_OPTION_VALS[2] => VAL_NO,
}
PERF_SAMPLES_VALS = TOUR_OPTION_VALS.clone
PERF_SAMPLES = TOUR_OPTIONS.clone
2015-05-27 15:08:04 +00:00
COUNT_FOLLOW = :count_follow
COUNT_RECORD = :count_record
COUNT_SESSION = :count_session
def self.json_schema
return @@jschema if @@jschema
@@jschema = {
2015-05-26 08:43:11 +00:00
TO_JOIN => BaseSearch.json_schema.merge({
KEY_SORT_ORDER => self::SORT_VALS[0],
2015-05-27 08:20:46 +00:00
KEY_BAND_TYPE => self::BAND_TYPE_VAL_STRS[0].to_s,
2015-05-27 04:10:05 +00:00
KEY_BAND_STATUS => BAND_STATUS_VALS[0],
KEY_PLAY_COMMIT => PLAY_COMMIT_VALS[0],
KEY_TOUR_OPTION => TOUR_OPTION_VALS[0],
}),
2015-05-26 08:43:11 +00:00
TO_HIRE => {
KEY_SORT_ORDER => self::HIRE_SORT_VALS[0],
KEY_GENRES => [],
KEY_GIGS => self::GIG_COUNTS[0].to_s,
2015-05-27 08:20:46 +00:00
KEY_BAND_STATUS => BAND_STATUS_VALS[0],
2015-06-04 07:38:16 +00:00
KEY_PERF_SAMPLES => self::PERF_SAMPLES_VALS[0],
KEY_HIRE_MAX_COST => 0,
2015-06-04 08:11:41 +00:00
KEY_HIRE_FREE => 0,
},
}
end
def self.search_filter_meta
return @@search_meta if @@search_meta
2015-05-26 08:43:11 +00:00
toJoinMeta = super(self.json_schema[TO_JOIN])
toJoinMeta.merge!({
2015-05-27 08:20:46 +00:00
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 }
})
2015-05-26 08:43:11 +00:00
toHireMeta = super(self.json_schema[TO_HIRE],
2015-05-20 09:24:53 +00:00
{ keys: HIRE_SORT_VALS, map: HIRE_SORT_ORDERS })
2015-05-21 05:47:14 +00:00
toHireMeta.merge!({
2015-06-03 06:41:21 +00:00
KEY_BAND_STATUS => { keys: BAND_STATUS_VALS, map: BAND_STATUS },
2015-05-21 05:47:14 +00:00
KEY_PERF_SAMPLES => { keys: PERF_SAMPLES_VALS, map: PERF_SAMPLES },
})
@@search_meta = {
2015-05-26 08:43:11 +00:00
TO_JOIN => toJoinMeta,
TO_HIRE => toHireMeta,
}
end
def self.search_target_class
2015-05-22 12:09:14 +00:00
Band
end
2015-05-22 12:09:14 +00:00
def _genres(rel, filter)
super(rel, filter)
end
def _concert_gigs(rel, filter)
2015-05-26 06:39:43 +00:00
gg = filter[KEY_GIGS].to_i
rel = rel.where(concert_count: gg) if 0 <= gg
2015-05-22 12:09:14 +00:00
rel
end
2015-05-27 04:10:05 +00:00
def _band_status(rel, filter)
case filter[KEY_BAND_STATUS]
2015-05-26 06:39:43 +00:00
when GenrePlayer::VIRTUAL_BAND
2015-05-27 04:10:05 +00:00
rel.where(band_status: GenrePlayer::VIRTUAL_BAND.sub('_band',''))
2015-05-26 06:39:43 +00:00
when GenrePlayer::TRADITIONAL_BAND
2015-05-27 04:10:05 +00:00
rel.where(band_status: GenrePlayer::TRADITIONAL_BAND.sub('_band',''))
2015-05-26 06:39:43 +00:00
else
rel
2015-05-22 12:09:14 +00:00
end
end
2015-05-27 02:58:04 +00:00
def _play_commitment(rel, filter)
2015-05-26 06:39:43 +00:00
unless ANY_VAL_STR == filter[KEY_PLAY_COMMIT]
rel = rel.where(play_commitment: filter[KEY_PLAY_COMMIT].to_i)
end
2015-05-22 12:09:14 +00:00
rel
end
2015-05-27 04:10:05 +00:00
def _touring_option(rel, filter)
2015-05-22 12:09:14 +00:00
case filter[KEY_TOUR_OPTION]
2015-05-27 02:58:04 +00:00
when VAL_YES
2015-05-26 06:39:43 +00:00
rel.where(touring_option: true)
2015-05-27 02:58:04 +00:00
when VAL_NO
2015-05-26 06:39:43 +00:00
rel.where(touring_option: false)
else
rel
2015-05-22 12:09:14 +00:00
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
2015-05-22 12:09:14 +00:00
end
def _max_cost(rel, filter)
2015-05-26 06:39:43 +00:00
if 0 < (max_cost = filter[KEY_HIRE_MAX_COST].to_i)
col = Band.arel_table[:gig_minimum]
2015-05-27 02:58:04 +00:00
rel = rel.where(col.lteq(max_cost)).where(col.gt(0))
2015-05-26 06:39:43 +00:00
end
2015-05-22 12:09:14 +00:00
rel
end
def _free_gigs(rel, filter)
2015-05-27 02:58:04 +00:00
case filter[KEY_HIRE_FREE]
when VAL_YES
2015-05-26 06:39:43 +00:00
rel.where(free_gigs: true)
2015-05-27 02:58:04 +00:00
when VAL_NO
2015-05-26 06:39:43 +00:00
rel.where(free_gigs: false)
else
rel
2015-05-22 12:09:14 +00:00
end
end
2015-05-27 08:20:46 +00:00
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])
2015-05-26 06:39:43 +00:00
else
rel
end
2015-05-22 12:09:14 +00:00
end
2015-05-27 08:20:46 +00:00
def _sort_order(rel, filter)
val = filter[KEY_SORT_ORDER]
if 'distance' == val || val.blank?
# TODO: bring back search by distance
#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")
#rel = rel.order('st_distance(my_geo.geog, other_geo.geog)')
2015-05-27 08:20:46 +00:00
elsif 'price_asc' == val
rel = rel.order('gig_minimum ASC')
elsif 'price_desc' == val
rel = rel.order('gig_minimum DESC')
end
rel
end
2015-05-22 12:09:14 +00:00
def do_search(filter)
rel = Band.unscoped
filter.keys.each do |fkey|
mname = "_#{fkey}"
2015-05-26 08:43:11 +00:00
if self.respond_to?(mname)
2015-05-22 12:09:14 +00:00
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
2015-05-27 15:08:04 +00:00
def _process_results_page(_results)
@results = _results
if user
2015-05-27 15:08:04 +00:00
@user_counters = @results.inject({}) { |hh,val| hh[val.id] = {}; hh }
# this gets counts for each search result
@results.each do |bb|
2015-05-27 15:08:04 +00:00
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
2015-06-03 06:41:21 +00:00
def _count(band, key)
if mm = @user_counters[band.id]
2015-05-27 15:08:04 +00:00
return mm[key]
end if @user_counters
0
end
public
2015-06-03 06:41:21 +00:00
def follow_count(band)
_count(band, COUNT_FOLLOW)
end
2015-06-03 06:41:21 +00:00
def record_count(band)
_count(band, COUNT_RECORD)
end
2015-06-03 06:41:21 +00:00
def session_count(band)
_count(band, COUNT_SESSION)
end
2015-06-03 06:41:21 +00:00
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
2015-05-26 08:43:11 +00:00
def reset_search_results(subtype=TO_JOIN)
reset_filter(subtype)
search_results_page(subtype)
end
2015-05-26 08:43:11 +00:00
def self.search_filter_json(user, subtype=TO_JOIN)
self.user_search_filter(user).json[subtype]
end
2015-05-26 08:43:11 +00:00
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
2015-05-26 08:43:11 +00:00
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
2015-05-27 15:08:04 +00:00
_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)
2015-05-27 08:20:46 +00:00
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
2015-06-04 07:38:16 +00:00
if (val = filter[KEY_BAND_TYPE]) != ANY_VAL_STR
str = _add_description(str, "Band type = #{BAND_TYPES[val]}")
2015-06-04 07:38:16 +00:00
end if filter.has_key?(KEY_BAND_TYPE)
2015-05-27 08:20:46 +00:00
if (val = filter[KEY_BAND_STATUS]) != ANY_VAL_STR
str = _add_description(str, "Band status = #{BAND_STATUS[val]}")
2015-06-04 07:38:16 +00:00
end if filter.has_key?(KEY_BAND_STATUS)
2015-05-27 08:20:46 +00:00
if (val = filter[KEY_PLAY_COMMIT]) != ANY_VAL_STR
str = _add_description(str, "Play commitment = #{PLAY_COMMITS[val]}")
2015-06-04 07:38:16 +00:00
end if filter.has_key?(KEY_PLAY_COMMIT)
2015-05-27 08:20:46 +00:00
if (val = filter[KEY_TOUR_OPTION]) != ANY_VAL_STR
str = _add_description(str, "Touring options = #{TOUR_OPTIONS[val]}")
2015-06-04 07:38:16 +00:00
end if filter.has_key?(KEY_TOUR_OPTION)
2015-05-27 08:20:46 +00:00
if (val = filter[KEY_PERF_SAMPLES]) != ANY_VAL_STR
str = _add_description(str, "Performance samples = #{PERF_SAMPLES[val]}")
2015-06-04 07:38:16 +00:00
end if filter.has_key?(KEY_PERF_SAMPLES)
2015-05-27 08:20:46 +00:00
if (val = filter[KEY_HIRE_MAX_COST].to_i) > 0
str = _add_description(str, "Maximum gig cost = $#{val}")
2015-06-04 07:38:16 +00:00
end if filter.has_key?(KEY_HIRE_MAX_COST)
2015-06-04 08:11:41 +00:00
if 0 < filter[KEY_HIRE_FREE]
str = _add_description(str, "Bands playing free gigs")
2015-06-04 07:38:16 +00:00
end if filter.has_key?(KEY_HIRE_FREE)
2015-05-27 08:20:46 +00:00
if (val = filter[KEY_GIGS].to_i) != GIG_COUNTS[0]
str = _add_description(str, "Concert gigs = #{GIG_LABELS[val]}")
2015-06-04 07:38:16 +00:00
end if filter.has_key?(KEY_GIGS)
2015-05-27 08:20:46 +00:00
if 0 < (val = filter[KEY_GENRES]).length
gstr = "Genres = "
2015-05-27 08:20:46 +00:00
genres = Genre.where(["id IN (?)", val]).order('description').pluck(:description)
gstr += genres.join(', ')
str = _add_description(str, gstr)
2015-06-04 07:38:16 +00:00
end if filter.has_key?(KEY_GENRES)
if 0 < ((val = filter[KEY_INSTRUMENTS]) || '').length
istr = "Instruments = "
2015-05-27 08:20:46 +00:00
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)
2015-05-27 08:20:46 +00:00
end
str = _add_description(str, istr)
2015-06-04 07:38:16 +00:00
end if filter.has_key?(KEY_INSTRUMENTS)
str = "Current Search: #{str}"
2015-05-27 08:20:46 +00:00
str
end
end
end