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? # 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)') 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