diff --git a/db/manifest b/db/manifest
index bc10f39b4..50a8269d5 100755
--- a/db/manifest
+++ b/db/manifest
@@ -78,4 +78,5 @@ add_user_bio.sql
users_geocoding.sql
recordings_public_launch.sql
notification_band_invite.sql
-band_photo_filepicker.sql
\ No newline at end of file
+band_photo_filepicker.sql
+bands_geocoding.sql
diff --git a/db/up/bands_geocoding.sql b/db/up/bands_geocoding.sql
new file mode 100644
index 000000000..cf612ac0e
--- /dev/null
+++ b/db/up/bands_geocoding.sql
@@ -0,0 +1,4 @@
+ALTER TABLE bands ADD COLUMN lat NUMERIC(15,10);
+ALTER TABLE bands ADD COLUMN lng NUMERIC(15,10);
+
+UPDATE bands SET country = 'US' WHERE country = 'USA';
diff --git a/ruby/lib/jam_ruby/models/band.rb b/ruby/lib/jam_ruby/models/band.rb
index 640f6c2a0..3ce6fee36 100644
--- a/ruby/lib/jam_ruby/models/band.rb
+++ b/ruby/lib/jam_ruby/models/band.rb
@@ -2,8 +2,8 @@ module JamRuby
class Band < ActiveRecord::Base
attr_accessible :name, :website, :biography, :city, :state,
- :country, :original_fpfile_photo, :cropped_fpfile_photo,
- :cropped_s3_path_photo, :crop_selection_photo, :photo_url
+ :country, :original_fpfile_photo, :cropped_fpfile_photo,
+ :cropped_s3_path_photo, :crop_selection_photo, :photo_url
attr_accessor :updating_photo
@@ -13,6 +13,8 @@ module JamRuby
validate :validate_photo_info
validates :biography, no_profanity: true
+ before_save :check_lat_lng
+
# musicians
has_many :band_musicians, :class_name => "JamRuby::BandMusician"
has_many :users, :through => :band_musicians, :class_name => "JamRuby::User"
@@ -40,6 +42,9 @@ module JamRuby
has_many :music_sessions, :class_name => "JamRuby::MusicSession", :foreign_key => "band_id"
has_many :music_session_history, :class_name => "JamRuby::MusicSessionHistory", :foreign_key => "band_id", :inverse_of => :band
+ include Geokit::ActsAsMappable::Glue unless defined?(acts_as_mappable)
+ acts_as_mappable
+
def liker_count
return self.likers.size
end
@@ -96,11 +101,11 @@ module JamRuby
if hide_private
recordings = Recording.joins(:band_recordings)
- .where(:bands_recordings => {:band_id => "#{band_id}"}, :public => true)
+ .where(:bands_recordings => {:band_id => "#{band_id}"}, :public => true)
else
recordings = Recording.joins(:band_recordings)
- .where(:bands_recordings => {:band_id => "#{band_id}"})
+ .where(:bands_recordings => {:band_id => "#{band_id}"})
end
return recordings
@@ -125,11 +130,10 @@ module JamRuby
# helper method for creating / updating a Band
def self.save(id, name, website, biography, city, state, country, genres, user_id, photo_url, logo_url)
-
user = User.find(user_id)
# new band
- if id.nil?
+ if id.blank?
# ensure person creating this Band is a Musician
unless user.musician?
@@ -139,7 +143,7 @@ module JamRuby
validate_genres(genres, false)
band = Band.new()
- # band update
+ # band update
else
validate_genres(genres, true)
band = Band.find(id)
@@ -168,35 +172,28 @@ module JamRuby
# country
band.country = country unless country.nil?
- # genres
- unless genres.nil?
- ActiveRecord::Base.transaction do
- # delete all genres for this band first
- unless band.id.nil? || band.id.length == 0
- band.genres.delete_all
- end
-
- # loop through each genre in the array and save to the db
- genres.each do |genre_id|
- g = Genre.find(genre_id)
- band.genres << g
- end
- end
- end
-
# photo url
band.photo_url = photo_url unless photo_url.nil?
# logo url
band.logo_url = logo_url unless logo_url.nil?
- band.updated_at = Time.now.getutc
- band.save
+ # band.updated_at = Time.now.getutc
+ band.save!
+ band.reload
+
+ # genres
+ unless genres.nil?
+ ActiveRecord::Base.transaction do
+ # delete all genres for this band first
+ band.genres.delete_all if id.present?
+ # loop through each genre in the array and save to the db
+ genres.each { |genre_id| band.genres << Genre.find(genre_id) }
+ end
+ end
# add the creator as the admin
- if id.nil?
- BandMusician.create(:band_id => band.id, :user_id => user_id, :admin => true)
- end
+ BandMusician.create(:band_id => band.id, :user_id => user_id, :admin => true) if id.blank?
return band
end
@@ -207,61 +204,83 @@ module JamRuby
cropped_s3_path = cropped_fpfile["key"]
return self.update_attributes(
- :original_fpfile_photo => original_fpfile,
- :cropped_fpfile_photo => cropped_fpfile,
- :cropped_s3_path_photo => cropped_s3_path,
- :crop_selection_photo => crop_selection,
- :photo_url => S3Util.url(aws_bucket, cropped_s3_path, :secure => false)
- )
+ :original_fpfile_photo => original_fpfile,
+ :cropped_fpfile_photo => cropped_fpfile,
+ :cropped_s3_path_photo => cropped_s3_path,
+ :crop_selection_photo => crop_selection,
+ :photo_url => S3Util.url(aws_bucket, cropped_s3_path, :secure => false)
+ )
end
def delete_photo(aws_bucket)
Band.transaction do
- unless self.cropped_s3_path.nil?
- S3Util.delete(aws_bucket, File.dirname(self.cropped_s3_path) + '/cropped.jpg')
- S3Util.delete(aws_bucket, self.cropped_s3_path)
+ unless self.cropped_s3_path_photo.nil?
+ S3Util.delete(aws_bucket, File.dirname(self.cropped_s3_path_photo) + '/cropped.jpg')
+ S3Util.delete(aws_bucket, self.cropped_s3_path_photo)
end
return self.update_attributes(
- :original_fpfile_photo => nil,
- :cropped_fpfile_photo => nil,
- :cropped_s3_path_photo => nil,
- :crop_selection_photo => nil,
- :photo_url => nil
- )
+ :original_fpfile_photo => nil,
+ :cropped_fpfile_photo => nil,
+ :cropped_s3_path_photo => nil,
+ :crop_selection_photo => nil,
+ :photo_url => nil
+ )
end
+ end
+ def check_lat_lng
+ if (city_changed? || state_changed? || country_changed?)
+ update_lat_lng
+ end
+ true
+ end
+
+ def update_lat_lng
+ if self.city
+ query = { :city => self.city }
+ query[:region] = self.state unless self.state.blank?
+ query[:country] = self.country unless self.country.blank?
+ if geo = MaxMindGeo.where(query).limit(1).first
+ if geo.lat && geo.lng && (self.lat != geo.lat || self.lng != geo.lng)
+ self.lat, self.lng = geo.lat, geo.lng
+ return true
+ end
+ end
+ end
+ self.lat, self.lng = nil, nil
+ false
end
private
- def self.validate_genres(genres, is_nil_ok)
- if is_nil_ok && genres.nil?
- return
- end
+ def self.validate_genres(genres, is_nil_ok)
+ if is_nil_ok && genres.nil?
+ return
+ end
- if genres.nil?
+ if genres.nil?
+ raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET
+ else
+ if genres.size < Limits::MIN_GENRES_PER_BAND
raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET
- else
- if genres.size < Limits::MIN_GENRES_PER_BAND
- raise JamRuby::JamArgumentError, ValidationMessages::GENRE_MINIMUM_NOT_MET
- end
+ end
- if genres.size > Limits::MAX_GENRES_PER_BAND
- raise JamRuby::JamArgumentError, ValidationMessages::GENRE_LIMIT_EXCEEDED
- end
+ if genres.size > Limits::MAX_GENRES_PER_BAND
+ raise JamRuby::JamArgumentError, ValidationMessages::GENRE_LIMIT_EXCEEDED
end
end
+ end
- def stringify_photo_info
- # fpfile comes in as a hash, which is a easy-to-use and validate form. However, we store it as a VARCHAR,
- # so we need t oconvert it to JSON before storing it (otherwise it gets serialized as a ruby object)
- # later, when serving this data out to the REST API, we currently just leave it as a string and make a JSON capable
- # client parse it, because it's very rare when it's needed at all
- self.original_fpfile_photo = original_fpfile_photo.to_json if !original_fpfile_photo.nil?
- self.cropped_fpfile_photo = cropped_fpfile_photo.to_json if !cropped_fpfile_photo.nil?
- self.crop_selection_photo = crop_selection_photo.to_json if !crop_selection_photo.nil?
- end
+ def stringify_photo_info
+ # fpfile comes in as a hash, which is a easy-to-use and validate form. However, we store it as a VARCHAR,
+ # so we need t oconvert it to JSON before storing it (otherwise it gets serialized as a ruby object)
+ # later, when serving this data out to the REST API, we currently just leave it as a string and make a JSON capable
+ # client parse it, because it's very rare when it's needed at all
+ self.original_fpfile_photo = original_fpfile_photo.to_json if !original_fpfile_photo.nil?
+ self.cropped_fpfile_photo = cropped_fpfile_photo.to_json if !cropped_fpfile_photo.nil?
+ self.crop_selection_photo = crop_selection_photo.to_json if !crop_selection_photo.nil?
+ end
end
end
diff --git a/ruby/lib/jam_ruby/models/max_mind_geo.rb b/ruby/lib/jam_ruby/models/max_mind_geo.rb
index 81868c4cd..713938552 100644
--- a/ruby/lib/jam_ruby/models/max_mind_geo.rb
+++ b/ruby/lib/jam_ruby/models/max_mind_geo.rb
@@ -40,7 +40,35 @@ module JamRuby
end
end
User.find_each { |usr| usr.update_lat_lng }
+ Band.find_each { |bnd| bnd.update_lat_lng }
end
+
+ def self.where_latlng(relation, params, current_user=nil)
+ if 0 < (distance = params[:distance].to_i)
+ latlng = []
+ if location_city = params[:city]
+ if geo = self.where(:city => params[:city]).limit(1).first
+ latlng = [geo.lat, geo.lng]
+ end
+ elsif current_user
+ if current_user.lat.nil? || current_user.lng.nil?
+ if params[:remote_ip] && (geo = self.ip_lookup(params[:remote_ip]))
+ latlng = [geo.lat, geo.lng] if geo.lat && geo.lng
+ end
+ else
+ latlng = [current_user.lat, current_user.lng]
+ end
+ elsif params[:remote_ip] && (geo = self.ip_lookup(params[:remote_ip]))
+ latlng = [geo.lat, geo.lng] if geo.lat && geo.lng
+ end
+ if latlng.present?
+ relation = relation.where(['lat IS NOT NULL AND lng IS NOT NULL'])
+ .within(distance, :origin => latlng)
+ end
+ end
+ relation
+ end
+
end
end
diff --git a/ruby/lib/jam_ruby/models/search.rb b/ruby/lib/jam_ruby/models/search.rb
index 63221d8aa..930bb109c 100644
--- a/ruby/lib/jam_ruby/models/search.rb
+++ b/ruby/lib/jam_ruby/models/search.rb
@@ -2,6 +2,7 @@ module JamRuby
# not a active_record model; just a search result
class Search
attr_accessor :bands, :musicians, :fans, :recordings, :friends, :search_type
+ attr_accessor :bands_filter, :musicians_filter
LIMIT = 10
# performs a site-white search
@@ -30,6 +31,8 @@ module JamRuby
@fans = []
@recordings = []
@friends = []
+ @musicians_filter = []
+ @bands_filter = []
if search_results.nil?
return
@@ -39,7 +42,6 @@ module JamRuby
if result.class == User
if result.musician
@musicians.push(result)
- @search_type = PARAM_MUSICIAN
else
@fans.push(result)
end
@@ -84,19 +86,23 @@ module JamRuby
attr_accessor :user_counters, :page_num, :page_count
PARAM_MUSICIAN = :srch_m
+ PARAM_BAND = :srch_b
- M_PER_PAGE = 10
+ B_PER_PAGE = M_PER_PAGE = 10
M_MILES_DEFAULT = 500
+ B_MILES_DEFAULT = 0
M_ORDER_FOLLOWS = ['Most Followed', :followed]
M_ORDER_PLAYS = ['Most Plays', :plays]
M_ORDER_PLAYING = ['Playing Now', :playing]
- M_ORDERINGS = [M_ORDER_FOLLOWS, M_ORDER_PLAYS, M_ORDER_PLAYING]
- M_ORDERING_KEYS = M_ORDERINGS.collect { |oo| oo[1] }
+ ORDERINGS = B_ORDERINGS = M_ORDERINGS = [M_ORDER_FOLLOWS, M_ORDER_PLAYS, M_ORDER_PLAYING]
+ B_ORDERING_KEYS = M_ORDERING_KEYS = M_ORDERINGS.collect { |oo| oo[1] }
- def self.musician_order_param(params)
+ DISTANCE_OPTS = B_DISTANCE_OPTS = M_DISTANCE_OPTS = [['Any', 0], [1000.to_s, 1000], [500.to_s, 500], [250.to_s, 250], [100.to_s, 100], [50.to_s, 50], [25.to_s, 25]]
+
+ def self.order_param(params, keys=M_ORDERING_KEYS)
ordering = params[:orderby]
- ordering.blank? ? M_ORDERING_KEYS[0] : M_ORDERING_KEYS.detect { |oo| oo.to_s == ordering }
+ ordering.blank? ? keys[0] : keys.detect { |oo| oo.to_s == ordering }
end
def self.musician_search(params={}, current_user=nil)
@@ -106,31 +112,10 @@ module JamRuby
.where(['minst.instrument_id = ? AND users.id IS NOT NULL', instrument])
end
- location_distance, location_city = params[:distance], params[:city]
- distance, latlng = nil, []
- if location_distance && location_city
- if geo = MaxMindGeo.where(:city => params[:city]).limit(1).first
- distance, latlng = location_distance, [geo.lat, geo.lng]
- end
- elsif current_user
- if current_user.lat.nil? || current_user.lng.nil?
- if params[:remote_ip] && (geo = MaxMindGeo.ip_lookup(params[:remote_ip]))
- latlng = [geo.lat, geo.lng] if geo.lat && geo.lng
- end
- else
- latlng = [current_user.lat, current_user.lng]
- end
- elsif params[:remote_ip] && (geo = MaxMindGeo.ip_lookup(params[:remote_ip]))
- latlng = [geo.lat, geo.lng] if geo.lat && geo.lng
- end
- if latlng.present?
- distance ||= location_distance || M_MILES_DEFAULT
- rel = rel.where(['lat IS NOT NULL AND lng IS NOT NULL'])
- .within(distance, :origin => latlng)
- end
+ rel = MaxMindGeo.where_latlng(rel, params, current_user)
sel_str = 'users.*'
- case ordering = self.musician_order_param(params)
+ case ordering = self.order_param(params)
when :plays # FIXME: double counting?
sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}"
rel = rel.joins("LEFT JOIN music_sessions AS sessions ON sessions.user_id = users.id")
@@ -150,9 +135,7 @@ module JamRuby
end
rel = rel.select(sel_str)
- perpage = [(params[:per_page] || M_PER_PAGE).to_i, 100].min
- page = [params[:page].to_i, 1].max
- rel = rel.paginate(:page => page, :per_page => perpage)
+ rel, page = self.relation_pagination(rel, params)
rel = rel.includes([:instruments, :followings, :friends])
objs = rel.all
@@ -161,6 +144,12 @@ module JamRuby
srch.musician_results_for_user(objs, current_user)
end
+ def self.relation_pagination(rel, params)
+ perpage = [(params[:per_page] || M_PER_PAGE).to_i, 100].min
+ page = [params[:page].to_i, 1].max
+ [rel.paginate(:page => page, :per_page => perpage), page]
+ end
+
RESULT_FOLLOW = :follows
RESULT_FRIEND = :friends
@@ -171,10 +160,10 @@ module JamRuby
COUNTERS = [COUNT_FRIEND, COUNT_FOLLOW, COUNT_RECORD, COUNT_SESSION]
def musician_results_for_user(results, user)
- @search_type, @musicians = PARAM_MUSICIAN, results
+ @search_type, @musicians_filter = PARAM_MUSICIAN, results
if user
@user_counters = results.inject({}) { |hh,val| hh[val.id] = []; hh }
- mids = "'#{@musicians.map(&:id).join("','")}'"
+ mids = "'#{@musicians_filter.map(&:id).join("','")}'"
# this gets counts for each search result on friends/follows/records/sessions
results.each do |uu|
@@ -258,5 +247,73 @@ module JamRuby
end
end
+ def self.band_search(params={}, current_user=nil)
+ rel = Band.scoped
+
+ unless (genre = params[:genre]).blank?
+ rel = Band.joins("RIGHT JOIN bands_genres AS bgenres ON bgenres.band_id = bands.id")
+ .where(['bgenres.genre_id = ? AND bands.id IS NOT NULL', genre])
+ end
+
+ rel = MaxMindGeo.where_latlng(rel, params, current_user)
+
+ sel_str = 'bands.*'
+ case ordering = self.order_param(params)
+ when :plays # FIXME: double counting?
+ sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}"
+ rel = rel.joins("LEFT JOIN music_sessions AS sessions ON sessions.band_id = bands.id")
+ .joins("LEFT JOIN recordings AS records ON records.band_id = bands.id")
+ .group("bands.id")
+ .order("play_count DESC, bands.created_at DESC")
+ when :followed
+ sel_str = "COUNT(follows) AS search_follow_count, #{sel_str}"
+ rel = rel.joins("LEFT JOIN bands_followers AS follows ON follows.band_id = bands.id")
+ .group("bands.id")
+ .order("COUNT(follows) DESC, bands.created_at DESC")
+ when :playing
+ rel = rel.joins("LEFT JOIN music_sessions_history AS msh ON msh.band_id = bands.id")
+ .where('msh.music_session_id IS NOT NULL AND msh.session_removed_at IS NULL')
+ .order("bands.created_at DESC")
+ end
+
+ rel = rel.select(sel_str)
+ rel, page = self.relation_pagination(rel, params)
+ rel = rel.includes([{ :users => :instruments }, :genres ])
+
+ objs = rel.all
+ srch = Search.new
+ srch.page_num, srch.page_count = page, objs.total_pages
+ srch.band_results_for_user(objs, current_user)
+ end
+
+ def band_results_for_user(results, user)
+ @search_type, @bands_filter = PARAM_BAND, results
+ if user
+ @user_counters = results.inject({}) { |hh,val| hh[val.id] = []; hh }
+ mids = "'#{@bands_filter.map(&:id).join("','")}'"
+
+ # this gets counts for each search result
+ results.each do |bb|
+ counters = { }
+ counters[COUNT_FOLLOW] = BandFollowing.where(:band_id => bb.id).count
+ counters[COUNT_RECORD] = Recording.where(:band_id => bb.id).count
+ counters[COUNT_SESSION] = MusicSession.where(:band_id => bb.id).count
+ @user_counters[bb.id] << counters
+ end
+
+ # this section determines follow/like/friend status for each search result
+ # so that action links can be activated or not
+
+ rel = Band.select("bands.id AS bid")
+ rel = rel.joins("LEFT JOIN bands_followers AS follows ON follows.follower_id = '#{user.id}'")
+ rel = rel.where(["bands.id IN (#{mids}) AND follows.band_id = bands.id"])
+ rel.all.each { |val| @user_counters[val.bid] << RESULT_FOLLOW }
+
+ else
+ @user_counters = {}
+ end
+ self
+ end
+
end
end
diff --git a/ruby/spec/jam_ruby/models/band_filter_search_spec.rb b/ruby/spec/jam_ruby/models/band_filter_search_spec.rb
new file mode 100644
index 000000000..51e43f403
--- /dev/null
+++ b/ruby/spec/jam_ruby/models/band_filter_search_spec.rb
@@ -0,0 +1,187 @@
+require 'spec_helper'
+
+describe 'Band search' do
+
+ before(:each) do
+ @geocode1 = FactoryGirl.create(:geocoder)
+ @geocode2 = FactoryGirl.create(:geocoder)
+ @bands = []
+ @bands << @band1 = FactoryGirl.create(:band)
+ @bands << @band2 = FactoryGirl.create(:band)
+ @bands << @band3 = FactoryGirl.create(:band)
+ @bands << @band4 = FactoryGirl.create(:band)
+
+ @bands.each do |bb|
+ FactoryGirl.create(:band_musician, :band => bb, :user => FactoryGirl.create(:user))
+ (rand(4)+1).downto(1) do |nn|
+ FactoryGirl.create(:band_musician, :band => bb, :user => FactoryGirl.create(:user))
+ end
+ end
+
+ end
+
+ context 'default filter settings' do
+
+ it "finds all bands" do
+ # expects all the bands
+ num = Band.count
+ results = Search.band_search({ :per_page => num })
+ expect(results.bands_filter.count).to eq(num)
+ end
+
+ it "finds bands with proper ordering" do
+ # the ordering should be create_at since no followers exist
+ expect(BandFollower.count).to eq(0)
+ results = Search.band_search({ :per_page => Band.count })
+ results.bands_filter.each_with_index do |uu, idx|
+ expect(uu.id).to eq(@bands.reverse[idx].id)
+ end
+ end
+
+ it "sorts bands by followers" do
+ users = []
+ 4.downto(1) { |nn| users << FactoryGirl.create(:user) }
+
+ # establish sorting order
+ @band4.followers.concat(users[1..-1])
+ @band3.followers.concat(users[1..3])
+ @band2.followers.concat(users[0])
+ @bands.map(&:reload)
+
+ expect(@band4.followers.count).to be 3
+ expect(BandFollower.count).to be 7
+
+ # refresh the order to ensure it works right
+ @band2.followers.concat(users[1..-1])
+ results = Search.band_search({ :per_page => @bands.size }, users[0])
+ expect(results.bands_filter[0].id).to eq(@band2.id)
+
+ # check the follower count for given entry
+ expect(results.bands_filter[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_search(params)
+ expect(results.bands_filter.count).to be 2
+ end
+
+ end
+
+ def make_session(band)
+ usr = band.users[0]
+ session = FactoryGirl.create(: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) }
+
+ # establish sorting order
+ @band1.followers.concat(users)
+ results = Search.band_search({},@band1)
+ uu = results.bands_filter.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_search({},@band1)
+ uu = results.bands_filter.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_search({ :orderby => 'plays' })
+ expect(results.bands_filter[0].id).to eq(@band2.id)
+ expect(results.bands_filter[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(:music_session_history, :music_session => session)
+
+ results = Search.band_search({ :orderby => 'playing' })
+ expect(results.bands_filter.count).to be 1
+ expect(results.bands_filter.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(:music_session_history, :music_session => session)
+ results = Search.band_search({ :orderby => 'playing' })
+ expect(results.bands_filter.count).to be 2
+ expect(results.bands_filter[0].id).to eq(@band4.id)
+ expect(results.bands_filter[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_search({ :genre => ggg.id })
+ results.bands_filter.each do |rr|
+ expect(rr.genres.detect { |gg| gg.id==ggg.id }.id).to eq(genre.id)
+ end
+ expect(results.bands_filter.count).to be 1
+ end
+
+ it "finds bands within a given distance of given location" do
+ num = Band.count
+ expect(@band1.lat).to_not be_nil
+ # short distance
+ results = Search.band_search({ :per_page => num,
+ :distance => 10,
+ :city => 'Apex' }, @band1)
+ expect(results.bands_filter.count).to be num
+ # long distance
+ results = Search.band_search({ :per_page => num,
+ :distance => 1000,
+ :city => 'Miami',
+ :state => 'FL' }, @band1)
+ expect(results.bands_filter.count).to be num
+ end
+
+ it "finds bands within a given distance of bands location" do
+ expect(@band1.lat).to_not be_nil
+ # uses the location of @band1
+ results = Search.band_search({ :distance => 10, :per_page => Band.count }, @band1)
+ expect(results.bands_filter.count).to be Band.count
+ end
+
+ it "finds no bands within a given distance of location" do
+ expect(@band1.lat).to_not be_nil
+ results = Search.band_search({ :distance => 10, :city => 'San Francisco' }, @band1)
+ expect(results.bands_filter.count).to be 0
+ end
+
+ end
+
+end
diff --git a/ruby/spec/jam_ruby/models/band_location_spec.rb b/ruby/spec/jam_ruby/models/band_location_spec.rb
new file mode 100644
index 000000000..fae562a53
--- /dev/null
+++ b/ruby/spec/jam_ruby/models/band_location_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe Band do
+
+ before do
+ @geocode1 = FactoryGirl.create(:geocoder)
+ @geocode2 = FactoryGirl.create(:geocoder)
+ @band = FactoryGirl.create(:band)
+ end
+
+ describe "with profile location data" do
+ it "should have lat/lng values" do
+ geo = MaxMindGeo.find_by_city(@band.city)
+ @band.lat.should == geo.lat
+ @band.lng.should == geo.lng
+ end
+ it "should have updated lat/lng values" do
+ @band.update_attributes({ :city => @geocode2.city,
+ :state => @geocode2.region,
+ :country => @geocode2.country,
+ })
+ geo = MaxMindGeo.find_by_city(@band.city)
+ @band.lat.should == geo.lat
+ @band.lng.should == geo.lng
+ end
+ end
+
+ describe "without location data" do
+ it "should have nil lat/lng values without address" do
+ @band.update_attributes({ :city => nil,
+ :state => nil,
+ :country => nil,
+ })
+ @band.lat.should == nil
+ @band.lng.should == nil
+ end
+ end
+
+end
diff --git a/ruby/spec/jam_ruby/models/band_search_spec.rb b/ruby/spec/jam_ruby/models/band_search_spec.rb
index 2af217b3f..40e26d50f 100644
--- a/ruby/spec/jam_ruby/models/band_search_spec.rb
+++ b/ruby/spec/jam_ruby/models/band_search_spec.rb
@@ -5,7 +5,9 @@ describe User do
let(:user) { FactoryGirl.create(:user) }
before(:each) do
-
+ @geocode1 = FactoryGirl.create(:geocoder)
+ @geocode2 = FactoryGirl.create(:geocoder)
+ @user = FactoryGirl.create(:user)
@band = Band.save(nil, "Example Band", "www.bands.com", "zomg we rock", "Apex", "NC", "US", ["hip hop"], user.id, nil, nil)
end
diff --git a/ruby/spec/jam_ruby/models/musician_search_spec.rb b/ruby/spec/jam_ruby/models/musician_search_spec.rb
index 6eda4ae28..955b2f04f 100644
--- a/ruby/spec/jam_ruby/models/musician_search_spec.rb
+++ b/ruby/spec/jam_ruby/models/musician_search_spec.rb
@@ -18,14 +18,14 @@ describe 'Musician search' do
# expects all the users
num = User.musicians.count
results = Search.musician_search({ :per_page => num })
- expect(results.musicians.count).to eq(num)
+ expect(results.musicians_filter.count).to eq(num)
end
it "finds musicians with proper ordering" do
# the ordering should be create_at since no followers exist
expect(UserFollower.count).to eq(0)
results = Search.musician_search({ :per_page => User.musicians.count })
- results.musicians.each_with_index do |uu, idx|
+ results.musicians_filter.each_with_index do |uu, idx|
expect(uu.id).to eq(@users.reverse[idx].id)
end
end
@@ -41,10 +41,10 @@ describe 'Musician search' do
# refresh the order to ensure it works right
@user2.followers.concat([@user3, @user4, @user2])
results = Search.musician_search({ :per_page => @users.size }, @user3)
- expect(results.musicians[0].id).to eq(@user2.id)
+ expect(results.musicians_filter[0].id).to eq(@user2.id)
# check the follower count for given entry
- expect(results.musicians[0].search_follow_count.to_i).not_to eq(0)
+ expect(results.musicians_filter[0].search_follow_count.to_i).not_to eq(0)
# check the follow relationship between current_user and result
expect(results.is_follower?(@user2)).to be true
end
@@ -53,7 +53,7 @@ describe 'Musician search' do
# make sure pagination works right
params = { :per_page => 2, :page => 1 }
results = Search.musician_search(params)
- expect(results.musicians.count).to be 2
+ expect(results.musicians_filter.count).to be 2
end
end
@@ -96,7 +96,7 @@ describe 'Musician search' do
Friendship.save(@user1.id, @user2.id)
# search on user2
results = Search.musician_search({}, @user2)
- friend = results.musicians.detect { |mm| mm.id == @user1.id }
+ friend = results.musicians_filter.detect { |mm| mm.id == @user1.id }
expect(friend).to_not be_nil
expect(results.friend_count(friend)).to be 1
@user1.reload
@@ -115,7 +115,7 @@ describe 'Musician search' do
expect(@user1.recordings.detect { |rr| rr == recording }).to_not be_nil
results = Search.musician_search({},@user1)
- uu = results.musicians.detect { |mm| mm.id == @user1.id }
+ uu = results.musicians_filter.detect { |mm| mm.id == @user1.id }
expect(uu).to_not be_nil
expect(results.record_count(uu)).to be 1
@@ -130,28 +130,28 @@ describe 'Musician search' do
make_recording(@user1)
# order results by num recordings
results = Search.musician_search({ :orderby => 'plays' }, @user2)
- expect(results.musicians[0].id).to eq(@user1.id)
+ expect(results.musicians_filter[0].id).to eq(@user1.id)
# add more data and make sure order still correct
make_recording(@user2); make_recording(@user2)
results = Search.musician_search({ :orderby => 'plays' }, @user2)
- expect(results.musicians[0].id).to eq(@user2.id)
+ expect(results.musicians_filter[0].id).to eq(@user2.id)
end
it "by now playing" do
# should get 1 result with 1 active session
make_session(@user3)
results = Search.musician_search({ :orderby => 'playing' }, @user2)
- expect(results.musicians.count).to be 1
- expect(results.musicians.first.id).to eq(@user3.id)
+ expect(results.musicians_filter.count).to be 1
+ expect(results.musicians_filter.first.id).to eq(@user3.id)
# should get 2 results with 2 active sessions
# sort order should be created_at DESC
make_session(@user4)
results = Search.musician_search({ :orderby => 'playing' }, @user2)
- expect(results.musicians.count).to be 2
- expect(results.musicians[0].id).to eq(@user4.id)
- expect(results.musicians[1].id).to eq(@user3.id)
+ expect(results.musicians_filter.count).to be 2
+ expect(results.musicians_filter[0].id).to eq(@user4.id)
+ expect(results.musicians_filter[1].id).to eq(@user3.id)
end
end
@@ -166,10 +166,10 @@ describe 'Musician search' do
ii = @user1.instruments.detect { |inst| inst.id == 'tuba' }
expect(ii).to_not be_nil
results = Search.musician_search({ :instrument => ii.id })
- results.musicians.each do |rr|
+ results.musicians_filter.each do |rr|
expect(rr.instruments.detect { |inst| inst.id=='tuba' }.id).to eq(ii.id)
end
- expect(results.musicians.count).to be 1
+ expect(results.musicians_filter.count).to be 1
end
it "finds musicians within a given distance of given location" do
@@ -179,26 +179,26 @@ describe 'Musician search' do
results = Search.musician_search({ :per_page => num,
:distance => 10,
:city => 'Apex' }, @user1)
- expect(results.musicians.count).to be num
+ expect(results.musicians_filter.count).to be num
# long distance
results = Search.musician_search({ :per_page => num,
:distance => 1000,
:city => 'Miami',
:state => 'FL' }, @user1)
- expect(results.musicians.count).to be num
+ expect(results.musicians_filter.count).to be num
end
it "finds musicians within a given distance of users location" do
expect(@user1.lat).to_not be_nil
# uses the location of @user1
results = Search.musician_search({ :distance => 10, :per_page => User.musicians.count }, @user1)
- expect(results.musicians.count).to be User.musicians.count
+ expect(results.musicians_filter.count).to be User.musicians.count
end
it "finds no musicians within a given distance of location" do
expect(@user1.lat).to_not be_nil
results = Search.musician_search({ :distance => 10, :city => 'San Francisco' }, @user1)
- expect(results.musicians.count).to be 0
+ expect(results.musicians_filter.count).to be 0
end
end
diff --git a/web/app/assets/javascripts/band_setup.js b/web/app/assets/javascripts/band_setup.js
index 5752a35e4..996190051 100644
--- a/web/app/assets/javascripts/band_setup.js
+++ b/web/app/assets/javascripts/band_setup.js
@@ -229,8 +229,8 @@
userNames = [];
userIds = [];
userPhotoUrls = [];
- //bandId = "1158c8b6-4c92-47dc-82bf-1e390c4f9b2c";
- bandId = $("#hdn-band-id").val();
+ bandId = "1158c8b6-4c92-47dc-82bf-1e390c4f9b2c";
+ //bandId = $("#hdn-band-id").val();
resetForm();
}
diff --git a/web/app/assets/javascripts/findBand.js b/web/app/assets/javascripts/findBand.js
new file mode 100644
index 000000000..a8a9714db
--- /dev/null
+++ b/web/app/assets/javascripts/findBand.js
@@ -0,0 +1,228 @@
+(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;
+
+ function loadBands(queryString) {
+ // squelch nulls and undefines
+ queryString = !!queryString ? queryString : "";
+
+ $.ajax({
+ type: "GET",
+ url: "/api/search.json?" + queryString,
+ async: true,
+ 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();
+ }
+ }
+ }
+
+ /**
+ * Render a list of bands
+ */
+ 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 bVals, bb, renderings='';
+ var instr_logos, instr;
+ var players, playerVals, aPlayer;
+
+ for (ii=0, len=bands.length; ii < len; ii++) {
+ bb = bands[ii];
+ instr_logos = '';
+ /*for (var jj=0, ilen=bb['instruments'].length; jj