wip - filter musician records

This commit is contained in:
Nuwan Chathuranga 2021-08-09 19:48:10 +05:30 committed by Nuwan
parent f57218f429
commit bab8d14798
8 changed files with 461 additions and 4 deletions

View File

@ -17,6 +17,7 @@ module JamRuby
KEY_INSTRUMENTS = 'instruments'
KEY_GIGS = 'concert_gigs'
KEY_SORT_ORDER = 'sort_order'
KEY_JOINED_WITHIN = 'joined_within_days'
SORT_VALS = %W{ latency distance }
SORT_ORDERS = {
@ -31,6 +32,15 @@ module JamRuby
SKILL_VALS[2] => 'Pro',
}
JOINED_WITHIN_VALS = [ANY_VAL_INT, 1, 7, 30, 90]
JOINED_WITHIN_LABELS = {
JOINED_WITHIN_VALS[0] => ANY_VAL_STR,
JOINED_WITHIN_VALS[1] => 'Within Last 1 Day',
JOINED_WITHIN_VALS[2] => 'Within Last 7 Day',
JOINED_WITHIN_VALS[3] => 'Within Last 30 Day',
JOINED_WITHIN_VALS[4] => 'Within Last 90 Day',
}
GIG_COUNTS = [ANY_VAL_INT, 0, 1, 2, 3, 4]
GIG_LABELS = {
GIG_COUNTS[0] => 'Any',
@ -52,6 +62,7 @@ module JamRuby
KEY_INSTRUMENTS => [],
KEY_GENRES => [],
KEY_GIGS => self::GIG_COUNTS[0].to_s,
KEY_JOINED_WITHIN => self::JOINED_WITHIN_VALS[0],
}
end
@ -129,7 +140,7 @@ module JamRuby
unless (instruments = query_data[KEY_INSTRUMENTS]).blank?
instrids = self.class.instrument_ids
instruments = instruments.select { |ii| instrids.has_key?(ii['instrument_id']) }
#debugger
unless instruments.blank?
instsql = "SELECT player_id FROM musicians_instruments WHERE (("
instsql += instruments.collect do |inst|
@ -159,6 +170,14 @@ module JamRuby
rel
end
def _joined_within(rel)
#debugger
if 0 < (val = json[KEY_JOINED_WITHIN].to_i)
rel = rel.where("created_at >= ?", val.days.ago.at_beginning_of_day)
end
rel
end
def _sort_order(rel)
end
@ -172,7 +191,7 @@ module JamRuby
rel
end
def search_results_page(filter=nil, page=1)
def search_results_page(filter=nil, page=1, user_ids = [])
if filter
self.data_blob = filter
self.save
@ -180,7 +199,7 @@ module JamRuby
filter = self.data_blob
end
rel = do_search(filter)
rel = do_search(filter, user_ids)
@page_number = [page.to_i, 1].max
rel = rel.paginate(:page => @page_number, :per_page => self.class::PER_PAGE)

View File

@ -143,8 +143,11 @@ module JamRuby
rel
end
def do_search(params={})
def do_search(params={}, user_ids = [])
rel = User.musicians.where('users.id <> ?', self.user.id)
if user_ids.any?
rel = rel.where(id: user_ids).where('users.id <> ?', self.user.id)
end
rel = Search.scope_schools_together(rel, self.user)
rel = self._genres(rel)
rel = self._ages(rel)
@ -154,6 +157,7 @@ module JamRuby
rel = self._instruments(rel)
rel = self._interests(rel)
rel = self._sort_order(rel)
rel = self._joined_within(rel)
rel
end

View File

@ -93,4 +93,133 @@ class ApiSearchController < ApiController
end
end
#filter users by first fetching users from latency graph database
#for the latency filter options and then quering the relational
#database for other filter options
def filter
latency_good = ActiveRecord::Type::Boolean.new.type_cast_from_user(params[:latency_good])
latency_fair = ActiveRecord::Type::Boolean.new.type_cast_from_user(params[:latency_fair])
latency_high = ActiveRecord::Type::Boolean.new.type_cast_from_user(params[:latency_high])
begin
user_ids = user_ids_by_latency(latency_good, latency_fair, latency_high)
filter_params = {
"sort_order"=>"latency",
"instruments"=>[],
"genres"=> [],
"concert_gigs"=>"-1",
"interests"=>"any",
"studio_sessions"=>"-1",
"ages"=>[],
"skill_level"=>"-1",
"joined_within_days"=>"any"
}
filter_params.merge!(genres: params[:genres]) unless params[:genres].blank?
beginner = ActiveRecord::Type::Boolean.new.type_cast_from_user(params[:proficiency_beginner])
intermediate = ActiveRecord::Type::Boolean.new.type_cast_from_user(params[:proficiency_intermediate])
expert = ActiveRecord::Type::Boolean.new.type_cast_from_user(params[:proficiency_expert])
proficiency_levels = []
proficiency_levels.push(1) if beginner
proficiency_levels.push(2) if intermediate
proficiency_levels.push(3) if expert
instruments = params[:instruments]
if instruments && instruments.any?
inst = []
instruments.each do |ii|
proficiency_levels.each do |pl|
inst << { instrument_id: ii, proficiency_level: pl}
end
end
filter_params.merge!(instruments: inst)
end
filter_params.merge!(joined_within_days: params[:joined_within_days]) unless params[:joined_within_days].blank?
sobj = MusicianSearch.user_search_filter(current_user)
@search = sobj.search_results_page(filter_params, [params[:page].to_i, 1].max, user_ids)
respond_with @search, responder: ApiResponder, status: 201, template: 'api_search/index'
rescue => exception
logger.debug("Latency exception: #{exception.message}")
Bugsnag.notify(exception) do |report|
report.severity = "error"
report.add_tab(:latency, {
params: params,
user_id: current_user.id,
name: current_user.name,
url: filter_latency_url,
})
end
render json: {}, status: 500
end
end
private
def filter_latency_url
"#{Rails.application.config.latency_data_host}/search_users"
end
def user_ids_by_latency(latency_good, latency_fair, latency_high)
user_ids = []
if latency_good || latency_fair || latency_high
uri = URI(filter_latency_url)
begin
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true if Rails.application.config.latency_data_host.start_with?("https://")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Basic #{Rails.application.config.latency_data_host_auth_code}"
req["Content-Type"] = "application/json"
req.body = {
my_user_id: current_user.id,
my_public_ip: request.remote_ip,
my_device_id: nil,
my_client_id: nil
}.to_json
response = http.request(req)
if response.is_a?(Net::HTTPOK) || response.is_a?(Net::HTTPSuccess)
graph_db_users = JSON.parse(response.body)["users"]
if latency_good || latency_fair || latency_high
graph_db_users.select! do |user|
total_latency = user["ars"]["total_latency"].to_f
(total_latency <= 40 && latency_good) ||
(total_latency > 40 && total_latency <= 80 && latency_fair) ||
(total_latency > 80 && latency_high)
end
end
user_ids = graph_db_users.map { | user | user["user_id"] }.uniq
return user_ids
else
logger.debug("Latency response failed: #{response}")
Bugsnag.notify("LatencyResponseFailed") do |report|
report.severity = "faliure"
report.add_tab(:latency, {
user_id: current_user.id,
name: current_user.name,
params: params,
url: filter_latency_url,
code: response.code,
body: response.body,
})
end
end
rescue => exception
raise exception
end
end
user_ids
end
end

View File

@ -33,6 +33,10 @@ class ApiUsersController < ApiController
render :json => {}, :status => 200
end
def me
render json: { first_name: current_user.first_name, last_name: current_user.last_name, name: current_user.name, photo_url: current_user.photo_url }, status: 200
end
def show
@user=lookup_user

View File

@ -378,6 +378,9 @@ Rails.application.routes.draw do
# validation
match '/data_validation' => 'api_users#validate_data', :via => :get
#current user data
match '/me' => 'api_users#me', :via => :get
match '/users' => 'api_users#index', :via => :get
match '/users' => 'api_users#create', :via => :post
match '/users/:id' => 'api_users#show', :via => :get, :as => 'api_user_detail'
@ -629,6 +632,7 @@ Rails.application.routes.draw do
match '/search/musicians' => 'api_search#musicians', :via => [:get, :post]
match '/search/bands' => 'api_search#bands', :via => [:get, :post]
match '/search/jam_tracks' => 'api_search#jam_tracks', :via => [:get, :post]
match '/filter' => 'api_search#filter', :via => [:post]
# join requests
match '/join_requests/:id' => 'api_join_requests#show', :via => :get, :as => 'api_join_request_detail'

View File

@ -0,0 +1,30 @@
require 'spec_helper'
require 'webmock/rspec'
describe ApiSearchController, type: :controller do
let (:user) { FactoryGirl.create(:user) }
let(:user1) { FactoryGirl.create(:user) }
before(:each) do
controller.current_user = user
end
describe "GET filter" do
let(:latency_data_uri) { /\S+\/search_users/ }
let(:response_body) { mock_latency_response([
{ user: user1, ars_total_latency: 1.0, ars_internet_latency: 0.4, audio_latency: 0.6 }
])}
it "success" do
stub_request(:post, latency_data_uri)
.to_return(body: response_body, status: 200)
get :filter, { latency_good: true, latency_fair: true, latency_high: true, format: 'json' }
response.should be_success
expect(response.content_type).to eq("application/json")
expect(response).to render_template(:index)
expect(response).to have_http_status(:created)
end
end
end

88
web/spec/fixtures/latency_users.json vendored Normal file
View File

@ -0,0 +1,88 @@
{
"users": [
{
"user_id": "963d5268-66b6-463a-a3ee-c97f274fc23f",
"first_name": "Peter",
"last_name": "Walker",
"audio_latency": 0.5,
"audio_latency_unknown": false,
"ars": {
"internet_latency": 0.5,
"total_latency": 1.0
},
"p2p": { "internet_latency": 7.0, "total_latency": 11.586167812347412 },
"wifi": null
},
{
"user_id": "6337dc99-5023-477f-8781-3a810bb35b61",
"first_name": "Kasun",
"last_name": "Kalhara",
"audio_latency": 15.0,
"audio_latency_unknown": false,
"ars": {
"internet_latency": 16.0,
"total_latency": 31.0
},
"p2p": { "internet_latency": null, "total_latency": null },
"wifi": null
},
{
"user_id": "feb671a3-1821-48f0-bc14-aa26cf98bb25",
"first_name": "David",
"last_name": "Wilson",
"audio_latency": 10.0,
"audio_latency_unknown": false,
"ars": {
"internet_latency": 30.0,
"total_latency": 40.0
},
"p2p": {
"internet_latency": 10.31944465637207,
"total_latency": 14.905612468719482
},
"wifi": null
},
{
"user_id": "fd467978-da75-421f-8e97-6406a784e87e",
"first_name": "Indrashapa",
"last_name": "Liyanage",
"audio_latency": 20.0,
"audio_latency_unknown": false,
"ars": {
"internet_latency": 21.0,
"total_latency": 41.0
},
"p2p": { "internet_latency": null, "total_latency": null },
"wifi": null
},
{
"user_id": "1",
"first_name": "Test",
"last_name": "User",
"audio_latency": 50.0,
"audio_latency_unknown": false,
"ars": {
"internet_latency": 50.0,
"total_latency": 100.0
},
"p2p": { "internet_latency": null, "total_latency": null },
"wifi": null
},
{
"user_id": "a09f9a7e-afb7-489d-870d-e13a336e0b97",
"first_name": "Seth",
"last_name": "Call",
"audio_latency": 75.0,
"audio_latency_unknown": false,
"ars": {
"internet_latency": 75.0,
"total_latency": 150.0
},
"p2p": { "internet_latency": 7.0, "total_latency": 11.586167812347412 },
"wifi": null
}
],
"my_audio_latency": 5.0,
"my_audio_latency_unknown": false
}

View File

@ -0,0 +1,179 @@
require 'spec_helper'
require 'webmock/rspec'
describe "Musician Filter API", type: :request do
let(:user) { FactoryGirl.create(:user) }
let(:user1) { FactoryGirl.create(:user) }
let(:user2) { FactoryGirl.create(:user) }
let(:user3) { FactoryGirl.create(:user) }
let(:user4) { FactoryGirl.create(:user) }
let(:user5) { FactoryGirl.create(:user) }
let(:user6) { FactoryGirl.create(:user) }
let(:latency_data_uri) { /\S+\/search_users/ }
let(:response_body) { mock_latency_response([
{ user: user1, ars_total_latency: 1.0, ars_internet_latency: 0.4, audio_latency: 0.6 }, #GOOD
{ user: user2, ars_total_latency: 40.0, ars_internet_latency: 25.0, audio_latency: 15.0 }, #GOOD
{ user: user3, ars_total_latency: 41.0, ars_internet_latency: 25, audio_latency: 16 }, #FAIR
{ user: user4, ars_total_latency: 80.0, ars_internet_latency: 40, audio_latency: 40.0 }, #FAIR
{ user: user5, ars_total_latency: 81.0, ars_internet_latency: 41, audio_latency: 40 }, #HIGH
{ user: user6, ars_total_latency: 100.0, ars_internet_latency: 50.0, audio_latency: 50.0 } #HIGH
])
}
let(:pop) { Genre.find_by_id('pop') }
let(:rap) { Genre.find_by_id('rap') }
let(:rock) { Genre.find_by_id('rock') }
before(:each) do
#ActiveMusicSession.delete_all
User.delete_all
stub_request(:post, latency_data_uri)
.with(:headers => {'Accept'=>'*/*', 'Content-Type'=>'application/json', 'User-Agent'=>'Ruby'})
.to_return( body: response_body, status: 200)
end
def login(user)
post '/sessions', "session[email]" => user.email, "session[password]" => user.password
end
before do
login(user)
end
it "get all musicians" do
get '/api/search/musicians.json?results=true'
expect(JSON.parse(response.body)["musicians"].size).to eq(6)
end
it "filter all musicians for all latency types" do
post '/api/filter.json', { latency_good: false, latency_fair: false, latency_high: false }
expect(JSON.parse(response.body)["musicians"].size).to eq(6)
end
it "filter GOOD latency users" do
post '/api/filter.json', { latency_good: true, latency_fair: false, latency_high: false }
expect(response.content_type).to eq("application/json")
expect(response).to render_template(:index)
expect(response).to have_http_status(:created)
expect(JSON.parse(response.body)["musicians"].size).to eq(2)
end
it "filter FAIR latency musicians" do
post '/api/filter.json', { latency_good: false, latency_fair: true, latency_high: false }
expect(JSON.parse(response.body)["musicians"].size).to eq(2)
end
it "filter HIGH latency musicians" do
post '/api/filter.json', { latency_good: false, latency_fair: false, latency_high: true }
expect(JSON.parse(response.body)["musicians"].size).to eq(2)
end
it "filter GOOD and FAIR latency musicians" do
post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: false }
expect(JSON.parse(response.body)["musicians"].size).to eq(4)
end
it "filter GOOD and HIGH latency musicians" do
post '/api/filter.json', { latency_good: true, latency_fair: false, latency_high: true }
expect(JSON.parse(response.body)["musicians"].size).to eq(4)
end
it "filter GOOD, FAIR and HIGH latency musicians" do
post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true }
expect(JSON.parse(response.body)["musicians"].size).to eq(6)
end
it "filter musicians by genres" do
#------ user1 and user2 are good latency ------
user1.genres = [pop, rap, rock]
user1.save!
user2.genres = [pop]
user2.save!
#-------- user3 and user4 are fair latency -----
user3.genres = [rock]
user3.save!
user4.genres = [pop, rock]
user4.save!
#-------- user5 and user6 are high latency -----
user5.genres = [rap, rock]
user5.save!
user6.genres = [rock]
user6.save!
#debugger
post '/api/filter.json', { latency_good: true, latency_fair: false, latency_high: false, genres: ['pop'] }
expect(JSON.parse(response.body)["musicians"].size).to eq(2)
post '/api/filter.json', { latency_good: false, latency_fair: true, latency_high: true, genres: ['rap'] }
expect(JSON.parse(response.body)["musicians"].size).to eq(1)
post '/api/filter.json', { latency_good: true, latency_fair: false, latency_high: true, genres: ['rock'] }
expect(JSON.parse(response.body)["musicians"].size).to eq(3)
post '/api/filter.json', { latency_good: false, latency_fair: true, latency_high: false, genres: ['pop', 'rock'] }
expect(JSON.parse(response.body)["musicians"].size).to eq(2)
end
it "filter musicians by instruments they play", focus: true do
user1.musician_instruments << FactoryGirl.create(:musician_instrument, player: user1, instrument: JamRuby::Instrument.find('drums'), proficiency_level: 1 )
user1.musician_instruments << FactoryGirl.create(:musician_instrument, player: user1, instrument: JamRuby::Instrument.find('violin'), proficiency_level: 2 )
user1.save!
user2.musician_instruments << FactoryGirl.create(:musician_instrument, player: user2, instrument: JamRuby::Instrument.find('violin'), proficiency_level: 1 )
user2.save!
user3.musician_instruments << FactoryGirl.create(:musician_instrument, player: user3, instrument: JamRuby::Instrument.find('drums'), proficiency_level: 2 )
user3.save!
post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true, instruments: ['drums'], proficiency_intermediate: true }
expect(JSON.parse(response.body)["musicians"].size).to eq(1)
post '/api/filter.json', { latency_good: false, latency_fair: true, latency_high: true, instruments: ['drums'], proficiency_beginner: true }
expect(JSON.parse(response.body)["musicians"].size).to eq(0)
post '/api/filter.json', { latency_good: true, latency_fair: false, latency_high: false, instruments: ['drums, violin'], proficiency_beginner: true }
expect(JSON.parse(response.body)["musicians"].size).to eq(2)
post '/api/filter.json', { latency_good: false, latency_fair: true, latency_high: false, instruments: ['drums'], proficiency_beginner: true }
expect(JSON.parse(response.body)["musicians"].size).to eq(0)
end
it "filter musicians by joined within day" do
user1.created_at = 1.day.ago
user1.save!
user2.created_at = 6.days.ago
user2.save!
user3.created_at = 7.days.ago
user3.save!
user4.created_at = 29.days.ago
user4.save!
user5.created_at = 30.days.ago
user5.save!
user6.created_at = 31.days.ago
user6.save!
post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: true, joined_within_days: 1 }
expect(JSON.parse(response.body)["musicians"].size).to eq(1)
post '/api/filter.json', { latency_good: true, latency_fair: false, latency_high: false, joined_within_days: 7 }
expect(JSON.parse(response.body)["musicians"].size).to eq(2)
post '/api/filter.json', { latency_good: true, latency_fair: true, latency_high: false, joined_within_days: 7 }
expect(JSON.parse(response.body)["musicians"].size).to eq(3)
post '/api/filter.json', { latency_good: false, latency_fair: true, latency_high: false, joined_within_days: 29 }
expect(JSON.parse(response.body)["musicians"].size).to eq(2)
end
end