merge develop

This commit is contained in:
Brian Smith 2014-05-29 00:20:09 -04:00
commit 7a4ad83cef
120 changed files with 7762 additions and 623 deletions

View File

@ -1,7 +1,7 @@
source 'http://rubygems.org'
source 'https://jamjam:blueberryjam@int.jamkazam.com/gems/'
devenv = ENV["BUILD_NUMBER"].nil? || ENV["TEST_WWW"] == "1"
devenv = ENV["BUILD_NUMBER"].nil?
if devenv
gem 'jam_db', :path=> "../db/target/ruby_package"
@ -72,9 +72,6 @@ gem 'postgres_ext', '1.0.0'
gem 'resque_mailer'
gem 'rest-client'
gem 'geokit-rails'
gem 'postgres_ext', '1.0.0'
group :libv8 do
gem 'libv8', "~> 3.11.8"
end
@ -105,10 +102,6 @@ group :development, :test do
gem 'rspec-rails'
gem 'guard-rspec', '0.5.5'
gem 'jasmine', '1.3.1'
gem 'pry'
gem 'pry-remote'
gem 'pry-stack_explorer'
gem 'pry-debugger'
gem 'execjs', '1.4.0'
gem 'therubyracer' #, '0.11.0beta8'
gem 'factory_girl_rails', '4.1.0'
@ -120,4 +113,12 @@ end
group :test do
gem 'simplecov', '~> 0.7.1'
gem 'simplecov-rcov'
gem 'capybara-webkit'
gem 'capybara-screenshot'
gem 'poltergeist'
end
gem 'pry'
gem 'pry-remote'
gem 'pry-stack_explorer'
gem 'pry-debugger'

View File

@ -38,7 +38,7 @@ ActiveAdmin.register_page "Dashboard" do
end
end
end
end
end
end
# column do

221
admin/app/admin/feeds.rb Normal file
View File

@ -0,0 +1,221 @@
ActiveAdmin.register_page 'Feed' do
content do
# get user information via params
user_id = nil
user_id = params[:feed][:user_id] if params[:feed] && params[:feed][:user_id] != ''
user_name = 'All'
user_name = User.find(user_id).to_label if user_id
render :partial => 'form', locals: {user_name: user_name, user_id: user_id }
page = (params[:page] ||= 1).to_i
per_page = 10
offset = (page - 1) * per_page
# get feed ids
where_sql = ''
where_sql = "where user_id = '#{user_id}'" if user_id
sql_feed_ids = "SELECT id, 'music_sessions' as type, created_at FROM music_sessions #{where_sql}
UNION ALL
SELECT DISTINCT recording_id as id, 'recordings' as type, created_at FROM recorded_tracks #{where_sql}
UNION ALL
SELECT id, 'diagnostics' as type, created_at FROM diagnostics #{where_sql}
ORDER BY created_at DESC
OFFSET #{offset}
LIMIT #{per_page};"
sql_feed_count = "SELECT COUNT(*) FROM (
SELECT id, 'music_sessions' as type, created_at FROM music_sessions #{where_sql}
UNION ALL
SELECT DISTINCT recording_id as id, 'recordings' as type, created_at FROM recorded_tracks #{where_sql}
UNION ALL
SELECT id, 'diagnostics' as type, created_at FROM diagnostics #{where_sql}
ORDER BY created_at DESC
) AS IDS;"
feed_count = ActiveRecord::Base.connection.execute(sql_feed_count).values[0][0].to_i
id_types = ActiveRecord::Base.connection.execute(sql_feed_ids).values
@feed_pages = WillPaginate::Collection.create(page, per_page) do |pager|
pager.total_entries = feed_count
pager.replace(id_types)
end
div class: 'feed-pagination' do
will_paginate @feed_pages
end
recordings = []
sessions = []
diagnostics = []
id_types.each do |id_and_type|
if id_and_type[1] == "music_sessions"
sessions << JamRuby::MusicSession.find(id_and_type[0])
elsif id_and_type[1] == "recordings"
recordings << JamRuby::Recording.find(id_and_type[0])
elsif id_and_type[1] == "diagnostics"
diagnostics << JamRuby::Diagnostic.find(id_and_type[0])
else
raise "Unknown type returned from feed ids"
end
end
columns do
column do
panel "Music Sessions - #{user_name}" do
if sessions.count > 0
table_for(sessions) do
column :creator do |msh|
link_to msh.creator.to_label, admin_feed_path({feed: {user_id: msh.creator.id}})
end
column :created_at do |msh| msh.created_at.strftime('%b %d %Y, %H:%M') end
column :duration do |msh| "#{msh.duration_minutes.round(2)} minutes" end
column :members do |msh|
uu = msh.unique_users
if uu.length > 0
uu.each do |u|
span link_to u.to_label + ', ', admin_feed_path({feed: {user_id: u.id}})
end
else
span para 'No members'
end
end
column :band do |msh| auto_link(msh.band, msh.band.try(:name)) end
column :fan_access do |msh| msh.fan_access end
column :plays do |msh| msh.plays.count end
column :likes do |msh| msh.likes.count end
column :comments do |msh|
if msh.comment_count > 0
text_node "(#{msh.comment_count}) "
msh.comments.each do |comment|
text_node comment.user.to_label + ', '
end
else
span para 'No comments'
end
end
end
else
span class: 'text-center' do
para 'No session activities.'
end
end
end
panel "Recordings - #{user_name}" do
if recordings.count > 0
table_for(recordings) do
column :starter do |rec|
link_to rec.owner.to_label, admin_feed_path({feed: {user_id: rec.owner.id}})
end
column :mixes do |rec|
ul do
mixes = rec.mixes
if mixes.count > 0
mixes.each do |mix|
li do
text_node "Created At: #{mix.created_at.strftime('%b %d %Y, %H:%M')}, "
text_node "Started At: #{mix.started_at.strftime('%b %d %Y, %H:%M')}, "
text_node "Completed At: #{mix.completed_at.strftime('%b %d %Y, %H:%M')}, "
text_node "Error Count: #{mix.error_count}, "
text_node "Error Reason: #{mix.error_reason}, "
text_node "Error Detail: #{mix.error_detail}, "
text_node "Download Count: #{mix.download_count}, "
if !mix.nil? && !mix[:ogg_url].nil?
span link_to 'Download OGG', mix.sign_url(3600, 'ogg')
else
text_node 'OGG download not available'
end
if !mix.nil? && !mix[:mp3_url].nil?
span link_to 'Download MP3', mix.sign_url(3600, 'mp3')
else
text_node 'MP3 download not available'
end
end
end
else
span para 'No mixes'
end
end
end
column :recorded_tracks do |rec|
ul do
rts = rec.recorded_tracks
if rts.count > 0
rts.each do |gt|
li do
span link_to gt.musician.to_label, admin_feed_path({feed: {user_id: gt.musician.id}})
span ", #{gt.instrument_id}, "
span "Download Count: #{gt.download_count}, "
span "Fully uploaded: #{gt.fully_uploaded}, "
span "Upload failures: #{gt.upload_failures}, "
span "Part failures: #{gt.part_failures}, "
if gt[:url]
# span link_to 'Download', gt.sign_url(3600)
else
span 'No track available'
end
end
end
else
span para 'No recorded tracks'
end
end
end
column :claimed_recordings do |rec|
ul do
crs = rec.claimed_recordings
if crs.count > 0
crs.each do |cr|
li do
span cr.name
span link_to cr.user.to_label, admin_feed_path({feed: {user_id: cr.user.id}})
span ", Public: #{cr.is_public}"
end
end
else
span para 'No claimed recordings'
end
end
end
end
else
span class: 'text-center' do
para 'No recording activities.'
end
end
end
panel "Diagnostics - #{user_name}" do
if diagnostics.count > 0 then
table_for(diagnostics) do
column :user do |d|
span link_to d.user.to_label, admin_feed_path({feed: {user_id: d.user.id}})
end
column :created_at do |d| d.created_at.strftime('%b %d %Y, %H:%M') end
column :type
column :creator
column :data do |d|
span style: "white-space: pre;" do
begin
JSON.pretty_generate(JSON.parse(d.data))
rescue
d.data
end
end
end
end
else
span class: 'text-center' do
para 'No diagnostic activities.'
end
end
end
end
end
div class: 'feed-pagination' do
will_paginate @feed_pages
end
end
end

View File

@ -103,7 +103,7 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
autocomplete :user, :email, :full => true, :display_value => :autocomplete_display_name
def get_autocomplete_items(parameters)
items = User.select("DISTINCT email, first_name, last_name, id").where(["email ILIKE ? OR first_name ILIKE ? OR last_name ILIKE ?", "%#{parameters[:term]}%", "%#{parameters[:term]}%", "%#{parameters[:term]}%"])
User.select("email, first_name, last_name, id").where(["email ILIKE ? OR first_name ILIKE ? OR last_name ILIKE ?", "%#{parameters[:term]}%", "%#{parameters[:term]}%", "%#{parameters[:term]}%"])
end
def create

View File

@ -9,6 +9,7 @@
/*
*= require jquery.ui.all
*= require custom
*/
// Active Admin's got SASS!
@import "active_admin/mixins";

View File

@ -3,3 +3,23 @@
font-size:small;
color:lightgray;
}
.text-center {
text-align: center;
}
.feed-pagination {
height: 20px;
margin-bottom: 15px;
.pagination {
float: left !important;
ul {
list-style-type: none;
li {
float: left;
}
}
}
}

View File

@ -0,0 +1,6 @@
<%= semantic_form_for :feed, url: admin_feed_path, method: :get do |f| %>
<%= f.inputs do %>
<%= f.input :user, :as => :autocomplete, :url => autocomplete_user_email_admin_users_path, :input_html => { :id_element => "#feed_user_id" } %>
<%= f.input :user_id, :as => :hidden %>
<% end %>
<% end %>

0
admin/log/phantomjs.out Normal file
View File

View File

@ -46,4 +46,131 @@ FactoryGirl.define do
description { |n| "Instrument #{n}" }
end
factory :genre, :class => JamRuby::Genre do
description { |n| "Genre #{n}" }
end
factory :music_session, :class => JamRuby::MusicSession do
sequence(:name) { |n| "Music Session #{n}" }
sequence(:description) { |n| "Music Session Description #{n}" }
fan_chat true
fan_access true
approval_required false
musician_access true
legal_terms true
language 'english'
legal_policy 'standard'
genre JamRuby::Genre.first
association :creator, :factory => :user
end
factory :music_session_user_history, :class => JamRuby::MusicSessionUserHistory do
ignore do
history nil
user nil
end
music_session_id { history.id }
user_id { user.id }
sequence(:client_id) { |n| "Connection #{n}" }
end
factory :recorded_track, :class => JamRuby::RecordedTrack do
instrument JamRuby::Instrument.first
sound 'stereo'
sequence(:client_id) { |n| "client_id-#{n}"}
sequence(:track_id) { |n| "track_id-#{n}"}
sequence(:client_track_id) { |n| "client_track_id-#{n}"}
md5 'abc'
length 1
fully_uploaded true
association :user, factory: :user
association :recording, factory: :recording
end
factory :recording, :class => JamRuby::Recording do
association :owner, factory: :user
association :music_session, factory: :active_music_session
factory :recording_with_track do
before(:create) { |recording, evaluator|
recording.recorded_tracks << FactoryGirl.create(:recorded_track, recording: recording, user: evaluator.owner)
}
end
end
factory :claimed_recording, :class => JamRuby::ClaimedRecording do
sequence(:name) { |n| "name-#{n}" }
sequence(:description) { |n| "description-#{n}" }
is_public true
association :genre, factory: :genre
association :user, factory: :user
before(:create) { |claimed_recording|
claimed_recording.recording = FactoryGirl.create(:recording_with_track, owner: claimed_recording.user)
}
end
factory :mix, :class => JamRuby::Mix do
started_at Time.now
completed_at Time.now
ogg_md5 'abc'
ogg_length 1
sequence(:ogg_url) { |n| "recordings/ogg/#{n}" }
mp3_md5 'abc'
mp3_length 1
sequence(:mp3_url) { |n| "recordings/mp3/#{n}" }
completed true
before(:create) {|mix|
user = FactoryGirl.create(:user)
mix.recording = FactoryGirl.create(:recording_with_track, owner: user)
mix.recording.claimed_recordings << FactoryGirl.create(:claimed_recording, user: user, recording: mix.recording)
}
end
factory :diagnostic, :class => JamRuby::Diagnostic do
type JamRuby::Diagnostic::NO_HEARTBEAT_ACK
creator JamRuby::Diagnostic::CLIENT
data Faker::Lorem.sentence
association :user, factory: :user
end
factory :active_music_session_no_user_history, :class => JamRuby::ActiveMusicSession do
association :creator, factory: :user
ignore do
name "My Music Session"
description "Come Music Session"
fan_chat true
fan_access true
approval_required false
musician_access true
legal_terms true
genre JamRuby::Genre.first
band nil
end
before(:create) do |session, evaluator|
music_session = FactoryGirl.create(:music_session, name: evaluator.name, description: evaluator.description, fan_chat: evaluator.fan_chat,
fan_access: evaluator.fan_access, approval_required: evaluator.approval_required, musician_access: evaluator.musician_access,
genre: evaluator.genre, creator: evaluator.creator, band: evaluator.band)
session.id = music_session.id
end
factory :active_music_session do
after(:create) { |session|
FactoryGirl.create(:music_session_user_history, :history => session.music_session, :user => session.creator)
}
factory :music_session_with_mount do
association :mount, :factory => :icecast_mount
end
end
end
end

View File

@ -0,0 +1,88 @@
require 'spec_helper'
describe 'Feeds' do
subject { page }
before(:each) do
MusicSession.delete_all
Recording.delete_all
Diagnostic.delete_all
User.delete_all
end
let(:admin) { FactoryGirl.create(:admin) }
let(:user) { FactoryGirl.create(:user) }
let(:music_session) { FactoryGirl.create(:music_session, :creator => user) }
let(:recording) { FactoryGirl.create(:recording_with_track, :owner => user) }
let(:diagnostic) { FactoryGirl.create(:diagnostic, :user => user) }
context 'empty dashboard' do
before(:each) do
visit admin_feed_path
end
it { should have_selector('h2', text: 'Feed') }
it 'has no feeds' do
should have_selector('p', text: 'No session activities.')
should have_selector('p', text: 'No recording activities.')
should have_selector('p', text: 'No diagnostic activities.')
end
end
context 'admin enters a user name' do
before(:each) do
user.touch
visit admin_feed_path
end
it 'auto-completes with email + full name', :js => true do
within('form.feed') do
fill_in 'feed_user', with: user.email[0..3]
end
page.execute_script %Q{ $('form.feed input#feed_user').trigger('focus') }
page.execute_script %Q{ $('form.feed input#feed_user').trigger('keydown') }
find('a.ui-corner-all', text: user.to_label).trigger(:click)
should have_selector('form.feed #feed_user', user.to_label)
should have_selector('form.feed #feed_user_id[value="' + user.id + '"]', visible:false)
end
end
context 'with existing activities' do
before(:each) do
music_session.touch
recording.touch
diagnostic.touch
visit admin_feed_path
end
it 'shows session, recording, diagnostic' do
should have_selector("tr#jam_ruby_music_session_#{music_session.id}")
should have_selector("tr#jam_ruby_recording_#{recording.id}")
should have_selector("tr#jam_ruby_diagnostic_#{diagnostic.id}")
end
it 'shows activities for one user', :js => true do
within('form.feed') do
fill_in 'feed_user', with: user.email[0..3]
end
page.execute_script %Q{ $('form.feed input#feed_user').trigger('focus') }
page.execute_script %Q{ $('form.feed input#feed_user').trigger('keydown') }
find('a.ui-corner-all', text: user.to_label).trigger(:click)
should have_selector('form.feed #feed_user', user.to_label)
should have_selector('form.feed #feed_user_id[value="' + user.id + '"]', visible:false)
page.execute_script %Q{ $('form.feed').trigger('submit') }
should have_selector("tr#jam_ruby_music_session_#{music_session.id}")
should have_selector("tr#jam_ruby_recording_#{recording.id}")
should have_selector("tr#jam_ruby_diagnostic_#{diagnostic.id}")
end
end
end

View File

@ -22,6 +22,9 @@ require 'rspec/autorun'
# load capybara
require 'capybara/rails'
require 'capybara/rspec'
require 'capybara-screenshot/rspec'
require 'capybara/poltergeist'
#include Rails.application.routes.url_helpers
@ -30,6 +33,11 @@ require 'capybara/rails'
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
Capybara.register_driver :poltergeist do |app|
driver = Capybara::Poltergeist::Driver.new(app, { debug: false, phantomjs_logger: File.open('log/phantomjs.out', 'w') })
end
Capybara.javascript_driver = :poltergeist
Capybara.default_wait_time = 10
RSpec.configure do |config|
# ## Mock Framework
@ -46,7 +54,7 @@ RSpec.configure do |config|
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
config.use_transactional_fixtures = false
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of

View File

@ -0,0 +1,30 @@
module Snapshot
SS_PATH = 'snapshots.html'
def set_up_snapshot(filepath = SS_PATH)
@size = [1280, 720] #arbitrary
@file = File.new(filepath, "w+")
@file.puts "<HTML><BODY BGCOLOR=grey>"
@file.puts "<H1>Snapshot #{ENV["BUILD_NUMBER"]} - #{@size[0]}x#{@size[1]}</H1>"
end
def snapshot_example
page.driver.resize(@size[0], @size[1])
@file.puts "<H3>Example name: #{get_description}</H3><BR><BR>"
end
def snap!(title = get_description)
base64 = page.driver.render_base64(:png)
@file.puts '<H3>' + title + '</H3>'
@file.puts '<img alt="' + title +'" src="data:image/png;base64,' + base64 + '" />'
@file.puts '<BR><BR><BR>'
end
def tear_down_snapshot
@file.puts "</BODY></HTML>"
@file.close()
end
end

5
atlassian-ide-plugin.xml Normal file
View File

@ -0,0 +1,5 @@
<atlassian-ide-plugin>
<project-configuration id="1">
<servers id="2" />
</project-configuration>
</atlassian-ide-plugin>

View File

@ -1 +1,10 @@
this is just for getting this maxmind data over there so i can use it.
source for iso3166-1 data:
http://dev.maxmind.com/static/csv/codes/iso3166.csv
source for iso3166-2 data (compatible):
http://geolite.maxmind.com/download/geoip/misc/region_codes.csv

View File

@ -1,13 +0,0 @@
AB,Alberta
BC,British Columbia
MB,Manitoba
NB,New Brunswick
NL,Newfoundland and Labrador
NS,Nova Scotia
NT,Northwest Territories
NU,Nunavut
ON,Ontario
PE,Prince Edward Island
QC,Quebec
SK,Saskatchewan
YT,Yukon
1 AB Alberta
2 BC British Columbia
3 MB Manitoba
4 NB New Brunswick
5 NL Newfoundland and Labrador
6 NS Nova Scotia
7 NT Northwest Territories
8 NU Nunavut
9 ON Ontario
10 PE Prince Edward Island
11 QC Quebec
12 SK Saskatchewan
13 YT Yukon

4106
db/geodata/region_codes.csv Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,57 +0,0 @@
AA,Armed Forces America
AE,Armed Forces
AP,Armed Forces Pacific
AK,Alaska
AL,Alabama
AR,Arkansas
AZ,Arizona
CA,California
CO,Colorado
CT,Connecticut
DC,District of Columbia
DE,Delaware
FL,Florida
GA,Georgia
GU,Guam
HI,Hawaii
IA,Iowa
ID,Idaho
IL,Illinois
IN,Indiana
KS,Kansas
KY,Kentucky
LA,Louisiana
MA,Massachusetts
MD,Maryland
ME,Maine
MI,Michigan
MN,Minnesota
MO,Missouri
MS,Mississippi
MT,Montana
NC,North Carolina
ND,North Dakota
NE,Nebraska
NH,New Hampshire
NJ,New Jersey
NM,New Mexico
NV,Nevada
NY,New York
OH,Ohio
OK,Oklahoma
OR,Oregon
PA,Pennsylvania
PR,Puerto Rico
RI,Rhode Island
SC,South Carolina
SD,South Dakota
TN,Tennessee
TX,Texas
UT,Utah
VA,Virginia
VI,Virgin Islands
VT,Vermont
WA,Washington
WI,Wisconsin
WV,West Virginia
WY,Wyoming
1 AA Armed Forces America
2 AE Armed Forces
3 AP Armed Forces Pacific
4 AK Alaska
5 AL Alabama
6 AR Arkansas
7 AZ Arizona
8 CA California
9 CO Colorado
10 CT Connecticut
11 DC District of Columbia
12 DE Delaware
13 FL Florida
14 GA Georgia
15 GU Guam
16 HI Hawaii
17 IA Iowa
18 ID Idaho
19 IL Illinois
20 IN Indiana
21 KS Kansas
22 KY Kentucky
23 LA Louisiana
24 MA Massachusetts
25 MD Maryland
26 ME Maine
27 MI Michigan
28 MN Minnesota
29 MO Missouri
30 MS Mississippi
31 MT Montana
32 NC North Carolina
33 ND North Dakota
34 NE Nebraska
35 NH New Hampshire
36 NJ New Jersey
37 NM New Mexico
38 NV Nevada
39 NY New York
40 OH Ohio
41 OK Oklahoma
42 OR Oregon
43 PA Pennsylvania
44 PR Puerto Rico
45 RI Rhode Island
46 SC South Carolina
47 SD South Dakota
48 TN Tennessee
49 TX Texas
50 UT Utah
51 VA Virginia
52 VI Virgin Islands
53 VT Vermont
54 WA Washington
55 WI Wisconsin
56 WV West Virginia
57 WY Wyoming

View File

@ -162,3 +162,9 @@ scheduled_sessions_3.sql
scheduled_sessions_cancel_all.sql
scheduled_sessions_started_at.sql
scheduled_sessions_open_rsvps.sql
add_last_jam_user_fields.sql
remove_lat_lng_user_fields.sql
update_get_work_for_larger_radius.sql
periodic_emails.sql
remember_extra_scoring_data.sql
indexing_for_regions.sql

View File

@ -0,0 +1,8 @@
ALTER TABLE users ADD COLUMN last_jam_addr BIGINT;
ALTER TABLE users ADD COLUMN last_jam_locidispid BIGINT;
-- (j)oin session as musician, (r)egister, (f)tue, (n)etwork test
ALTER TABLE users ADD COLUMN last_jam_updated_reason CHAR(1);
ALTER TABLE users ADD COLUMN last_jam_updated_at TIMESTAMP;

View File

@ -0,0 +1,2 @@
create index regions_countrycode_ndx on regions (countrycode);
create unique index regions_countrycode_region_ndx on regions (countrycode, region);

15
db/up/periodic_emails.sql Normal file
View File

@ -0,0 +1,15 @@
ALTER TABLE email_batches ADD COLUMN type VARCHAR(64) NOT NULL DEFAULT 'JamRuby::EmailBatch';
ALTER TABLE email_batches ADD COLUMN sub_type VARCHAR(64);
ALTER TABLE email_batches ALTER COLUMN body DROP NOT NULL;
ALTER TABLE email_batches ALTER COLUMN subject DROP NOT NULL;
ALTER TABLE email_batch_sets ADD COLUMN trigger_index INTEGER NOT NULL DEFAULT 0;
ALTER TABLE email_batch_sets ADD COLUMN sub_type VARCHAR(64);
ALTER TABLE email_batch_sets ADD COLUMN user_id VARCHAR(64);
CREATE INDEX email_batch_sets_progress_idx ON email_batch_sets(user_id, sub_type);
CREATE INDEX users_musician_email_idx ON users(subscribe_email, musician);
UPDATE users set first_social_promoted_at = first_liked_us;
ALTER TABLE users DROP column first_liked_us;

View File

@ -0,0 +1,3 @@
-- add column to hold the raw scoring data that the client posted.
alter table scores add column scoring_data varchar(4000);

View File

@ -0,0 +1,2 @@
alter table users drop column lat;
alter table users drop column lng;

View File

@ -0,0 +1,16 @@
DROP FUNCTION get_work (mylocidispid BIGINT);
CREATE FUNCTION get_work (mylocidispid BIGINT, myaddr BIGINT) RETURNS TABLE (client_id VARCHAR(64)) ROWS 5 VOLATILE AS $$
BEGIN
CREATE TEMPORARY TABLE foo (locidispid BIGINT, locid INT);
INSERT INTO foo SELECT DISTINCT locidispid, locidispid/1000000 FROM connections WHERE client_type = 'client';
DELETE FROM foo WHERE locidispid IN (SELECT DISTINCT blocidispid FROM current_scores WHERE alocidispid = mylocidispid AND (current_timestamp - score_dt) < INTERVAL '24 hours');
DELETE FROM foo WHERE locid NOT IN (SELECT locid FROM geoiplocations WHERE geog && st_buffer((SELECT geog FROM geoiplocations WHERE locid = mylocidispid/1000000), 4023360));
CREATE TEMPORARY TABLE bar (client_id VARCHAR(64), locidispid BIGINT, r DOUBLE PRECISION);
INSERT INTO bar SELECT l.client_id, l.locidispid, random() FROM connections l, foo f WHERE l.locidispid = f.locidispid AND l.client_type = 'client' AND addr != myaddr;
DROP TABLE foo;
DELETE FROM bar b WHERE r != (SELECT MAX(r) FROM bar b0 WHERE b0.locidispid = b.locidispid);
RETURN QUERY SELECT b.client_id FROM bar b ORDER BY r LIMIT 5;
DROP TABLE bar;
RETURN;
END;
$$ LANGUAGE plpgsql;

2
monitor/.rspec Executable file
View File

@ -0,0 +1,2 @@
--color
--format progress

11
monitor/Gemfile Executable file
View File

@ -0,0 +1,11 @@
source "https://rubygems.org"
gem "rspec"
gem "capybara"
gem "capybara-screenshot"
gem "poltergeist"
gem "launchy" # used for opening pages/screenshots when debugging
# these used only for the Fixnum#seconds method :-/
gem "i18n"
gem "activesupport"

59
monitor/Gemfile.lock Executable file
View File

@ -0,0 +1,59 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (3.1.12)
multi_json (~> 1.0)
addressable (2.3.6)
capybara (2.2.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
capybara-screenshot (0.3.19)
capybara (>= 1.0, < 3)
launchy
cliver (0.3.2)
diff-lcs (1.2.5)
i18n (0.6.9)
launchy (2.4.2)
addressable (~> 2.3)
mime-types (2.2)
mini_portile (0.5.3)
multi_json (1.10.0)
nokogiri (1.6.1)
mini_portile (~> 0.5.0)
nokogiri (1.6.1-x86-mingw32)
mini_portile (~> 0.5.0)
poltergeist (1.5.0)
capybara (~> 2.1)
cliver (~> 0.3.1)
multi_json (~> 1.0)
websocket-driver (>= 0.2.0)
rack (1.5.2)
rack-test (0.6.2)
rack (>= 1.0)
rspec (2.14.1)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-core (2.14.8)
rspec-expectations (2.14.5)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.6)
websocket-driver (0.3.3)
xpath (2.0.0)
nokogiri (~> 1.3)
PLATFORMS
ruby
x86-mingw32
DEPENDENCIES
activesupport
capybara
capybara-screenshot
i18n
launchy
poltergeist
rspec

View File

@ -1,9 +1,14 @@
require 'spec_helper'
# these tests MUST be idempotent and DO use actual production user accounts on www
www = 'http://www.jamkazam.com'
# these tests should be idempotent, and not spammy to other JK users
# because they DO use actual production user accounts on www (see the TestUsers below)
describe "Production site at #{www}", :test_www => true, :js => true, :type => :feature, :capybara_feature => true do
# Jenkins executes rspec on this folder every 15 minutes or so.
# SO don't use this to test something like a public session unless you want all the world to see
www = ENV['MONITOR_URL'] || 'http://www.jamkazam.com'
describe "Deployed site at #{www}", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
@ -28,7 +33,6 @@ describe "Production site at #{www}", :test_www => true, :js => true, :type =>
first_name + ' ' + last_name
end
end
user1 = TestUser.new({ email: 'anthony+jim@jamkazam.com', password: 'j4m!t3st3r', first_name: 'Jim', last_name: 'Smith', id: '68e8eea2-140d-44c1-b711-10d07ce70f96' })
user2 = TestUser.new({ email: 'anthony+john@jamkazam.com', password: 'j4m!t3st3r', first_name: 'John', last_name: 'Jones', id: '5bbcf689-2f73-452d-815a-c4f44e9e7f3e' })
@ -46,7 +50,7 @@ describe "Production site at #{www}", :test_www => true, :js => true, :type =>
end
it "is possible for #{user1} and #{user2} to see each other online, and to send messages" do
# this example heavily based on text_message_spec.rb
# this example heavily based on text_message_spec.rb in 'web'
in_client(user1) do
sign_in_poltergeist(user1)
@ -89,4 +93,3 @@ describe "Production site at #{www}", :test_www => true, :js => true, :type =>
end
end

43
monitor/spec/spec_helper.rb Executable file
View File

@ -0,0 +1,43 @@
require 'rubygems'
require 'active_support/time'
require 'capybara'
require 'capybara/rspec'
require 'capybara-screenshot'
require 'capybara-screenshot/rspec'
require 'capybara/poltergeist'
require 'support/client_interactions' #TODO: Strip out the helper methods that production_spec does not use
require 'support/utilities'
require 'support/stubs' # to make the JamXXXX warnings go away
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# Require this file using `require "spec_helper"` to ensure that it is only
# loaded once.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.run_all_when_everything_filtered = true
config.filter_run :focus
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = 'random'
config.include Capybara::DSL
end
Capybara.javascript_driver = :poltergeist
Capybara.default_driver = :poltergeist
Capybara.run_server = false # since we're testing an app outside this project
Capybara.default_wait_time = 15 # ^^ ditto
Capybara.configure do |config|
config.match = :one
config.exact_options = true
config.ignore_hidden_elements = true
config.visible_text_only = true
end

View File

@ -0,0 +1,126 @@
# methods here all assume you are in /client
NOTIFICATION_PANEL = '[layout-id="panelNotifications"]'
# enters text into the search sidebar
def site_search(text, options = {})
within('#searchForm') do
fill_in "search-input", with: text
end
if options[:expand]
page.driver.execute_script("jQuery('#searchForm').submit()")
find('h1', text:'search results')
end
end
# goes to the musician tile, and tries to find a musician
def find_musician(user)
visit "/client#/musicians"
timeout = 30
start = Time.now
# scroll by 100px until we find a user with the right id
while page.all('#end-of-musician-list').length == 0
page.execute_script('jQuery("#musician-filter-results").scrollTo("+=100px", 0, {axis:"y"})')
found = page.all(".result-list-button-wrapper[data-musician-id='#{user.id}']")
if found.length == 1
return found[0]
elsif found.length > 1
raise "ambiguous results in musician list"
end
if Time.now - start > timeout
raise "unable to find musician #{user} within #{timeout} seconds"
end
end
raise "unable to find musician #{user}"
end
def initiate_text_dialog(user)
# verify that the chat window is grayed out
site_search(user.first_name, expand: true)
find("#search-results a[user-id=\"#{user.id}\"][hoveraction=\"musician\"]", text: user.name).hover_intent
find('#musician-hover #btnMessage').trigger(:click)
find('h1', text: 'conversation with ' + user.name)
end
# sends a text message in the chat interface.
def send_text_message(msg, options={})
find('#text-message-dialog') # assert that the dialog is showing already
within('#text-message-dialog form.text-message-box') do
fill_in 'new-text-message', with: msg
end
find('#text-message-dialog .btn-send-text-message').trigger(:click)
find('#text-message-dialog .previous-message-text', text: msg) unless options[:should_fail]
# close the dialog if caller specified close_on_send
if options[:close_on_send]
find('#text-message-dialog .btn-close-dialog', text: 'CLOSE').trigger(:click) if options[:close_on_send]
page.should have_no_selector('#text-message-dialog')
end
if options[:should_fail]
find('#notification').should have_text(options[:should_fail])
end
end
# sends a chat message during session
def send_chat_message(msg)
find("[layout-id=\"panelChat\"] .chat-sender").should be_visible
within("[layout-id=\"panelChat\"] .chat-sender form.chat-message-form") do
fill_in 'new-chat-message', with: msg
end
find("[layout-id=\"panelChat\"] .chat-sender .btn-send-chat-message").trigger(:click)
end
def open_notifications
find("#{NOTIFICATION_PANEL} .panel-header").trigger(:click)
end
def hover_intent(element)
element.hover
element.hover
end
# forces document.hasFocus() to return false
def document_blur
page.evaluate_script(%{(function() {
// save original
if(!window.documentFocus) { window.documentFocus = window.document.hasFocus; }
window.document.hasFocus = function() {
console.log("document.hasFocus() returns false");
return false;
}
})()})
end
def document_focus
page.evaluate_script(%{(function() {
// save original
if(!window.documentFocus) { window.documentFocus = window.document.hasFocus; }
window.document.hasFocus = function() {
console.log("document.hasFocus() returns true");
return true;
}
})()})
end
# simulates focus event on window
def window_focus
page.evaluate_script(%{window.jQuery(window).trigger('focus');})
end
def close_websocket
page.evaluate_script("window.JK.JamServer.close(true)")
end

9
monitor/spec/support/stubs.rb Executable file
View File

@ -0,0 +1,9 @@
module JamRuby
class User
end
end
def signin_path
'/signin'
end

481
monitor/spec/support/utilities.rb Executable file
View File

@ -0,0 +1,481 @@
# add a hover_intent method to element, so that you can do find(selector).hover_intent
module Capybara
module Node
class Element
def attempt_hover
begin
hover
rescue => e
end
end
def hover_intent
hover
sleep 0.3
attempt_hover
sleep 0.3
attempt_hover
end
end
end
end
# holds a single test's session name's, mapped to pooled session names
$capybara_session_mapper = {}
# called in before (or after) test, to make sure each test run has it's own map of session names
def reset_session_mapper
$capybara_session_mapper.clear
Capybara.session_name = :default
end
# manages the mapped session name
def mapped_session_name(session_name)
return :default if session_name == :default # special treatment for the built-in session
$capybara_session_mapper[session_name] ||= 'session_' + $capybara_session_mapper.length.to_s
end
# in place of ever using Capybara.session_name directly,
# this utility is used to handle the mapping of session names in a way across all tests runs
def in_client(name)
session_name = name.class == JamRuby::User ? name.id : name
Capybara.session_name = mapped_session_name(session_name)
yield
end
def cookie_jar
Capybara.current_session.driver.browser.current_session.instance_variable_get(:@rack_mock_session).cookie_jar
end
#see also ruby/spec/support/utilities.rb
JAMKAZAM_TESTING_BUCKET = 'jamkazam-testing' #at least, this is the name given in jam-ruby
def wipe_s3_test_bucket
s3 = AWS::S3.new(:access_key_id => Rails.application.config.aws_access_key_id,
:secret_access_key => Rails.application.config.aws_secret_access_key)
test_bucket = s3.buckets[JAMKAZAM_TESTING_BUCKET]
if test_bucket.name == JAMKAZAM_TESTING_BUCKET
test_bucket.objects.each do |obj|
obj.delete
end
end
end
def sign_in(user)
visit signin_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "SIGN IN"
# Sign in when not using Capybara as well.
cookie_jar[:remember_token] = user.remember_token
end
def set_cookie(k, v)
case Capybara.current_session.driver
when Capybara::Poltergeist::Driver
page.driver.set_cookie(k,v)
when Capybara::RackTest::Driver
headers = {}
Rack::Utils.set_cookie_header!(headers,k,v)
cookie_string = headers['Set-Cookie']
Capybara.current_session.driver.browser.set_cookie(cookie_string)
when Capybara::Selenium::Driver
page.driver.browser.manage.add_cookie(:name=>k, :value=>v)
else
raise "no cookie-setter implemented for driver #{Capybara.current_session.driver.class.name}"
end
end
def sign_in_poltergeist(user, options = {})
validate = options[:validate]
validate = true if validate.nil?
visit signin_path
fill_in "Email Address:", with: user.email
fill_in "Password:", with: user.password
click_button "SIGN IN"
wait_until_curtain_gone
# presence of this means websocket gateway is not working
page.should have_no_selector('.no-websocket-connection') if validate
end
def sign_out()
if Capybara.javascript_driver == :poltergeist
page.driver.remove_cookie(:remember_token)
else
page.driver.browser.manage.remove_cookie :name => :remember_token
end
end
def sign_out_poltergeist(options = {})
find('.userinfo').hover()
click_link 'Sign Out'
should_be_at_root if options[:validate]
end
def should_be_at_root
find('h1', text: 'Play music together over the Internet as if in the same room')
end
def leave_music_session_sleep_delay
# add a buffer to ensure WSG has enough time to expire
sleep_dur = (Rails.application.config.websocket_gateway_connect_time_stale_browser +
Rails.application.config.websocket_gateway_connect_time_expire_browser) * 1.4
sleep sleep_dur
end
def wait_for_ajax(wait=Capybara.default_wait_time)
wait = wait * 10 #(because we sleep .1)
counter = 0
while page.execute_script("$.active").to_i > 0
counter += 1
sleep(0.1)
raise "AJAX request took longer than #{wait} seconds." if counter >= wait
end
end
# waits until the user object has been requested, which comes after the 'curtain' is lifted
# and after a call to /api/user/:id for the current user is called initially
def wait_until_user(wait=Capybara.default_wait_time)
wait = wait * 10 #(because we sleep .1)
counter = 0
# while page.execute_script("$('.curtain').is(:visible)") == "true"
# counter += 1
# sleep(0.1)
# raise "Waiting for user to populate took longer than #{wait} seconds." if counter >= wait
# end
end
def wait_until_curtain_gone
page.should have_no_selector('.curtain')
end
def wait_to_see_my_track
within('div.session-mytracks') {first('div.session-track.track')}
end
def repeat_for(duration=Capybara.default_wait_time)
finish_time = Time.now + duration.seconds
loop do
yield
sleep 1 # by default this will execute the block every 1 second
break if (Time.now > finish_time)
end
end
def determine_test_name(metadata, test_name_buffer = '')
description = metadata[:description_args]
if description.kind_of?(Array)
description = description[0]
end
if metadata.has_key? :example_group
return determine_test_name(metadata[:example_group], "#{description} #{test_name_buffer}")
else
return "#{description} #{test_name_buffer}"
end
end
def get_description
description = example.metadata[:description_args]
if description.kind_of?(Array)
description = description[0]
end
return description
end
# will select the value from a easydropdown'ed select element
def jk_select(text, select)
# the approach here is to find the hidden select element, and work way back up to the elements that need to be interacted with
find(select, :visible => false).find(:xpath, 'ancestor::div[contains(@class, "dropdown easydropdown")]').trigger(:click)
find(select, :visible => false).find(:xpath, 'ancestor::div[contains(@class, "dropdown-wrapper") and contains(@class, "easydropdown-wrapper") and contains(@class, "open")]').find('li', text: text).trigger(:click)
# works, but is 'cheating' because of visible = false
#select(genre, :from => 'genres', :visible => false)
end
# takes, or creates, a unique session description which is returned for subsequent calls to join_session to use
# in finding this session)
def create_session(options={})
creator = options[:creator] || FactoryGirl.create(:user)
unique_session_desc = options[:description] || "create_join_session #{SecureRandom.urlsafe_base64}"
genre = options[:genre] || 'Rock'
musician_access = options[:musician_access].nil? ? true : options[:musician_access]
fan_access = options[:fan_access].nil? ? true : options[:fan_access]
# create session in one client
in_client(creator) do
page.driver.resize(1500, 800) # makes sure all the elements are visible
emulate_client
sign_in_poltergeist creator
wait_until_curtain_gone
visit "/client#/createSession"
expect(page).to have_selector('h2', text: 'session info')
within('#create-session-form') do
fill_in('description', :with => unique_session_desc)
#select(genre, :from => 'genres', :visible => false) # this works, but is 'cheating' because easydropdown hides the native select element
jk_select(genre, '#create-session-form select[name="genres"]')
jk_select(musician_access ? 'Public' : 'Private', '#create-session-form select#musician-access')
jk_select(fan_access ? 'Public' : 'Private', '#create-session-form select#fan-access')
find('#create-session-form div.musician-access-false.iradio_minimal').trigger(:click)
find('div.intellectual-property ins').trigger(:click)
find('#btn-create-session').trigger(:click) # fails if page width is low
end
# verify that the in-session page is showing
expect(page).to have_selector('h2', text: 'my tracks')
find('#session-screen .session-mytracks .session-track')
end
return creator, unique_session_desc, genre
end
# this code assumes that there are no music sessions in the database. it should fail on the
# find('.join-link') call if > 1 session exists because capybara will complain of multiple matches
def join_session(joiner, options)
description = options[:description]
in_client(joiner) do
page.driver.resize(1500, 800) # makes sure all the elements are visible
emulate_client
sign_in_poltergeist joiner
wait_until_curtain_gone
visit "/client#/findSession"
# verify the session description is seen by second client
expect(page).to have_text(description)
find('.join-link').trigger(:click)
find('#btn-accept-terms').trigger(:click)
expect(page).to have_selector('h2', text: 'my tracks')
find('#session-screen .session-mytracks .session-track')
end
end
def emulate_client
page.driver.headers = { 'User-Agent' => ' JamKazam ' }
end
def create_join_session(creator, joiners=[], options={})
options[:creator] = creator
creator, unique_session_desc = create_session(options)
# find session in second client
joiners.each do |joiner|
join_session(joiner, description: unique_session_desc)
end
return creator, unique_session_desc
end
def formal_leave_by user
in_client(user) do
find('#session-leave').trigger(:click)
#find('#btn-accept-leave-session').trigger(:click)
expect(page).to have_selector('h2', text: 'feed')
end
end
def start_recording_with(creator, joiners=[], genre=nil)
create_join_session(creator, joiners, {genre: genre})
in_client(creator) do
find('#recording-start-stop').trigger(:click)
find('#recording-status').should have_content 'Stop Recording'
end
joiners.each do |joiner|
in_client(joiner) do
find('#notification').should have_content 'started a recording'
find('#recording-status').should have_content 'Stop Recording'
end
end
end
def stop_recording
find('#recording-start-stop').trigger(:click)
end
def assert_recording_finished
find('#recording-status').should have_content 'Make a Recording'
should have_selector('h1', text: 'recording finished')
end
def check_recording_finished_for(users=[])
users.each do |user|
in_client(user) do
assert_recording_finished
end
end
end
def claim_recording(name, description)
find('#recording-finished-dialog h1')
fill_in "claim-recording-name", with: name
fill_in "claim-recording-description", with: description
find('#keep-session-recording').trigger(:click)
page.should have_no_selector('h1', text: 'recording finished')
end
def set_session_as_private()
find('#session-settings-button').trigger(:click)
within('#session-settings-dialog') do
jk_select("Private", '#session-settings-dialog #session-settings-musician-access')
#select('Private', :from => 'session-settings-musician-access')
find('#session-settings-dialog-submit').trigger(:click)
end
# verify it's dismissed
page.should have_no_selector('h1', text: 'update session settings')
end
def set_session_as_public()
find('#session-settings-button').trigger(:click)
within('#session-settings-dialog') do
jk_select("Public", '#session-settings-dialog #session-settings-musician-access')
# select('Public', :from => 'session-settings-musician-access')
find('#session-settings-dialog-submit').trigger(:click)
end
# verify it's dismissed
page.should have_no_selector('h1', text: 'update session settings')
end
def get_options(selector)
find(selector, :visible => false).all('option', :visible => false).collect(&:text).uniq
end
def selected_genres(selector='#session-settings-genre')
page.evaluate_script("JK.GenreSelectorHelper.getSelectedGenres('#{selector}')")
end
def random_genre
['African',
'Ambient',
'Asian',
'Blues',
'Classical',
'Country',
'Electronic',
'Folk',
'Hip Hop',
'Jazz',
'Latin',
'Metal',
'Pop',
'R&B',
'Reggae',
'Religious',
'Rock',
'Ska',
'Other'].sample
end
def change_session_genre #randomly just change it
here = 'select.genre-list'
#wait_for_ajax
find('#session-settings-button').trigger(:click)
find('#session-settings-dialog') # ensure the dialog is visible
within('#session-settings-dialog') do
wait_for_ajax
@new_genre = get_options(here).-(["Select Genre"]).-(selected_genres).sample.to_s
jk_select(@new_genre, '#session-settings-dialog select[name="genres"]')
wait_for_ajax
find('#session-settings-dialog-submit').trigger(:click)
end
return @new_genre
end
def get_session_genre
here = 'select.genre-list'
find('#session-settings-button').trigger(:click)
wait_for_ajax
@current_genres = selected_genres
find('#session-settings-dialog-submit').trigger(:click)
return @current_genres.join(" ")
end
def find_session_contains?(text)
visit "/client#/findSession"
wait_for_ajax
within('#find-session-form') do
expect(page).to have_text(text)
end
end
def assert_all_tracks_seen(users=[])
users.each do |user|
in_client(user) do
users.reject {|u| u==user}.each do |other|
find('div.track-label', text: other.name)
#puts user.name + " is able to see " + other.name + "\'s track"
end
end
end
end
def view_profile_of user
id = user.kind_of?(JamRuby::User) ? user.id : user
# assume some user signed in already
visit "/client#/profile/#{id}"
wait_until_curtain_gone
end
def view_band_profile_of band
id = band.kind_of?(JamRuby::Band) ? band.id :
band.kind_of?(JamRuby::BandMusician) ? band.bands.first.id : band
visit "/client#/bandProfile/#{id}"
wait_until_curtain_gone
end
def sidebar_search_for string, category
visit "/client#/home"
find('#search-input')
fill_in "search", with: string
sleep 1
page.execute_script("JK.Sidebar.searchForInput()")
wait_for_ajax
jk_select(category, "search_text_type")
wait_for_ajax
end
def show_user_menu
page.execute_script("$('ul.shortcuts').show()")
#page.execute_script("JK.UserDropdown.menuHoverIn()")
end
# wait for the easydropdown version of the specified select element to become visible
def wait_for_easydropdown(select)
find(select, :visible => false).find(:xpath, 'ancestor::div[contains(@class, "dropdown easydropdown")]')
end
# defaults to enter key (13)
def send_key(selector, keycode = 13)
keypress_script = "var e = $.Event('keyup', { keyCode: #{keycode} }); jQuery('#{selector}').trigger(e);"
page.driver.execute_script(keypress_script)
end
def special_characters
["?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}"]
end
def garbage length
output = ''
length.times { output << special_characters.sample }
output.slice(0, length)
end

8
resetdb.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/sh -x
dropdb --if-exists jam
dropdb --if-exists jam_db_build
dropdb --if-exists jam_ruby_test
dropdb --if-exists jam_web_test
dropdb --if-exists jam_websockets_test
createdb -Upostgres jam || sudo su postgres -c "createdb jam"

View File

@ -4,7 +4,7 @@ unless ENV["LOCAL_DEV"] == "1"
source 'https://jamjam:blueberryjam@int.jamkazam.com/gems/'
end
devenv = ENV["BUILD_NUMBER"].nil? || ENV["TEST_WWW"] == "1"
devenv = ENV["BUILD_NUMBER"].nil?
if devenv
gem 'jam_db', :path=> "../db/target/ruby_package"

View File

@ -42,6 +42,7 @@ require "jam_ruby/resque/scheduled/icecast_config_retry"
require "jam_ruby/resque/scheduled/icecast_source_check"
require "jam_ruby/resque/scheduled/cleanup_facebook_signup"
require "jam_ruby/resque/scheduled/unused_music_notation_cleaner"
require "jam_ruby/resque/scheduled/user_progress_emailer"
require "jam_ruby/resque/google_analytics_event"
require "jam_ruby/mq_router"
require "jam_ruby/base_manager"
@ -145,10 +146,14 @@ require "jam_ruby/models/country"
require "jam_ruby/models/region"
require "jam_ruby/models/city"
require "jam_ruby/models/email_batch"
require "jam_ruby/models/email_batch_periodic"
require "jam_ruby/models/email_batch_new_musician"
require "jam_ruby/models/email_batch_progression"
require "jam_ruby/models/email_batch_set"
require "jam_ruby/models/email_error"
require "jam_ruby/app/mailers/async_mailer"
require "jam_ruby/app/mailers/batch_mailer"
require "jam_ruby/app/mailers/progress_mailer"
require "jam_ruby/models/affiliate_partner"
require "jam_ruby/models/chat_message"

View File

@ -0,0 +1,26 @@
module JamRuby
class ProgressMailer < ActionMailer::Base
include SendGrid
layout "user_mailer"
sendgrid_category :use_subject_lines
sendgrid_unique_args :env => Environment.mode
default :from => UserMailer::DEFAULT_SENDER
def send_reminder(batch_set)
user = batch_set.user
sendgrid_recipients([user.email])
sendgrid_substitute('@USERID', [user.id])
sendgrid_substitute(EmailBatchProgression::VAR_FIRST_NAME, [user.first_name])
mail(:to => user.email,
:subject => batch_set.subject,
:title => batch_set.title) do |format|
format.text { render batch_set.sub_type }
format.html { render batch_set.sub_type }
end
end
end
end

View File

@ -41,6 +41,7 @@
sendgrid_recipients([user.email])
sendgrid_substitute('@USERID', [user.id])
sendgrid_substitute(EmailBatchProgression::VAR_FIRST_NAME, [user.first_name])
mail(:to => user.email, :subject => "Welcome to JamKazam") do |format|
format.text
@ -106,9 +107,9 @@
sendgrid_recipients([user.email])
sendgrid_substitute('@USERID', [user.id])
sendgrid_unique_args :type => "new_musicians"
mail(:to => user.email, :subject => "JamKazam New Musicians in Your Area") do |format|
mail(:to => user.email, :subject => EmailBatchNewMusician.subject) do |format|
format.text
format.html
end

View File

@ -0,0 +1,14 @@
<% provide(:title, @title) %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<p>We noticed that you have downloaded the free JamKazam application, but you have not yet run the app. You can find other musicians and listen to sessions and recordings on our website, but you need to run the JamKazam application to play with other musicians online. If you are having trouble installing or running the app, please visit our JamKazam support center at the link below, and post a request for assistance so that we can help you get up and running.
</p>
<p>
<a style="color: #588C98;" href="https://jamkazam.desk.com/">https://jamkazam.desk.com</a>
</p>
<p>-- Team JamKazam
</p>

View File

@ -0,0 +1,7 @@
<% provide(:title, @title) %>
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
We noticed that you have downloaded the free JamKazam application, but you have not yet run the app. You can find other musicians and listen to sessions and recordings on our website, but you need to run the JamKazam application to play with other musicians online. If you are having trouble installing or running the app, please visit our JamKazam support center at the link below, and post a request for assistance so that we can help you get up and running.
-- Team JamKazam

View File

@ -0,0 +1,18 @@
<% provide(:title, @title) %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<p>We noticed that you have registered as a JamKazam musician, but you have not yet downloaded and started using the free JamKazam application. You can find other musicians and listen to sessions and recordings on our website, but you need the free JamKazam application to play with other musicians online. Please click the link below to go to the download page for the free JamKazam application, or visit our JamKazam support center so that we can help you get up and running.
</p>
<p>
<a style="color: #588C98;" href="http://www.jamkazam.com/downloads">Go to Download Page</a>
</p>
<p>
<a style="color: #588C98;" href="https://jamkazam.desk.com">Go to Support Center</a>
</p>
<p>-- Team JamKazam
</p>

View File

@ -0,0 +1,11 @@
<% provide(:title, @title) %>
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
We noticed that you have registered as a JamKazam musician, but you have not yet downloaded and started using the free JamKazam application. You can find other musicians and listen to sessions and recordings on our website, but you need the free JamKazam application to play with other musicians online. Please click the link below to go to the download page for the free JamKazam application, or visit our JamKazam support center so that we can help you get up and running.
Go to Download Page: http://www.jamkazam.com/downloads
Go to Support Center: https://jamkazam.desk.com
-- Team JamKazam

View File

@ -0,0 +1,19 @@
<% provide(:title, @title) %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<p>We noticed that you have not yet successfully set up your audio gear and passed the JamKazam latency and input/output audio gear tests. This means that you cannot yet play in online sessions with other musicians. If you are having trouble with this step, please click the link below for a knowledge base article that can help you get past this hurdle. If the test says your audio gear is not fast enough, or if your audio quality sounds poor, or if you are just confused, its very likely the tips in this article will help you get things set up and optimized so you can start playing online.
</p>
<p><a style="color: #588C98;" href="http://bit.ly/1i4Uul4">http://bit.ly/1i4Uul4</a>
</p>
<p>And if this knowledge base article does not get you fixed up, please visit our JamKazam support center at the link below, and post a request for assistance so that we can help you get up and running:
</p>
<p><a style="color: #588C98;" href="https://jamkazam.desk.com">https://jamkazam.desk.com</a>
</p>
<p>-- Team JamKazam
</p>

View File

@ -0,0 +1,13 @@
<% provide(:title, @title) %>
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
We noticed that you have not yet successfully set up your audio gear and passed the JamKazam latency and input/output audio gear tests. This means that you cannot yet play in online sessions with other musicians. If you are having trouble with this step, please click the link below for a knowledge base article that can help you get past this hurdle. If the test says your audio gear is not fast enough, or if your audio quality sounds poor, or if you are just confused, its very likely the tips in this article will help you get things set up and optimized so you can start playing online.
http://bit.ly/1i4Uul4
And if this knowledge base article does not get you fixed up, please visit our JamKazam support center at the link below, and post a request for assistance so that we can help you get up and running:
https://jamkazam.desk.com
-- Team JamKazam

View File

@ -0,0 +1,31 @@
<% provide(:title, @title) %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<p>We noticed that you havent yet played in a JamKazam session with multiple musicians that lasted long enough to be productive or fun. Since thats the whole point of the service, we wanted to reach out to see if we can help you. Please take a quick look at the suggestions below to see if they help you!
</p>
<p>Find Other Musicians on JamKazam<br />
Its still very early in our companys development, so we dont have zillions of users online on our service yet. If you click Find Session, you will often not find a good session to join, both due to the number of musicians online at any given time, and also because you wont see private sessions where groups of musicians dont want to be interrupted in their sessions.
</p>
<p>If you are having trouble getting into sessions, wed suggest you click the Musicians tile on the home screen of the app or the website: <a href="http://www.jamkazam.com/client#/musicians">Go To Musicians Page</a>
</p>
<p>This will display the JamKazam musicians sorted by latency to you - in other words, you can see which musicians have good network connections to you. Any musicians with green and yellow latency scores have good enough connections to support a play session with you. We recommend that read the profiles of these musicians to find others with shared musical interests and good network connections to you, and then use the Message button to say hi and see if they are interested in playing with you. If they are, use the Connect button to “friend” them on JamKazam, and use the Message button to set up a time to meet online for a session.
</p>
<p>Invite Your Friends to Play<br />
One of the best ways to connect and play with others is to invite your friends from the “real world” to join you on JamKazam, and then set up a time to meet online and get into a session together. To do this, just go to www.jamkazam.com and sign in. Then move your mouse over your picture or name in the upper right corner of the screen. A menu will be displayed. Click the Invite Friends option, and then you can click Facebook, Google, or Email to easily invite your friends from different services to join you on JamKazam.
</p>
<p>Troubleshoot Session Problems<br />
If you are having audio quality problems or other issues when you get into a session, please click the link below to visit our support center, and check the knowledge base articles under the Troubleshooting header to find solutions. And if that doesnt work, please post a request for assistance in the support center so that we can help you get up and running:
</p>
<p><a style="color: #588C98;" href="https://jamkazam.desk.com">https://jamkazam.desk.com</a>
</p>
<p>-- Team JamKazam
</p>

View File

@ -0,0 +1,22 @@
<% provide(:title, @title) %>
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
We noticed that you havent yet played in a JamKazam session with multiple musicians that lasted long enough to be productive or fun. Since thats the whole point of the service, we wanted to reach out to see if we can help you. Please take a quick look at the suggestions below to see if they help you!
Find Other Musicians on JamKazam
Its still very early in our companys development, so we dont have zillions of users online on our service yet. If you click Find Session, you will often not find a good session to join, both due to the number of musicians online at any given time, and also because you wont see private sessions where groups of musicians dont want to be interrupted in their sessions.
If you are having trouble getting into sessions, wed suggest you click the Musicians tile on the home screen of the app or the website: http://www.jamkazam.com/client#/musicians
This will display the JamKazam musicians sorted by latency to you - in other words, you can see which musicians have good network connections to you. Any musicians with green and yellow latency scores have good enough connections to support a play session with you. We recommend that read the profiles of these musicians to find others with shared musical interests and good network connections to you, and then use the Message button to say hi and see if they are interested in playing with you. If they are, use the Connect button to “friend” them on JamKazam, and use the Message button to set up a time to meet online for a session.
Invite Your Friends to Play
One of the best ways to connect and play with others is to invite your friends from the “real world” to join you on JamKazam, and then set up a time to meet online and get into a session together. To do this, just go to www.jamkazam.com and sign in. Then move your mouse over your picture or name in the upper right corner of the screen. A menu will be displayed. Click the Invite Friends option, and then you can click Facebook, Google, or Email to easily invite your friends from different services to join you on JamKazam.
Troubleshoot Session Problems
If you are having audio quality problems or other issues when you get into a session, please click the link below to visit our support center, and check the knowledge base articles under the Troubleshooting header to find solutions. And if that doesnt work, please post a request for assistance in the support center so that we can help you get up and running:
https://jamkazam.desk.com
-- Team JamKazam

View File

@ -0,0 +1,27 @@
<% provide(:title, @title) %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<p>We noticed that you havent yet connected with any friends on JamKazam. Connecting with friends is the best way to help you get into sessions with other musicians on JamKazam. Here are a couple of good ways to connect with others.
</p>
<p>Find Other Musicians on JamKazam<br />
To find and connect with other musicians who are already on JamKazam, wed suggest you click the Musicians tile on the home screen of the app or the website: <a href="http://www.jamkazam.com/client#/musicians">Go To Musicians Page</a>
</p>
<p>This will display the JamKazam musicians sorted by latency to you - in other words, you can see which musicians have good network connections to you. Any musicians with green and yellow latency scores have good enough connections to support a play session with you. We recommend that you read the profiles of these musicians to find others with shared musical interests and good network connections to you, and then use the Message button to say hi and see if they are interested in playing with you. If they are, use the Connect button to “friend” them on JamKazam, and use the Message button to set up a time to meet online for a session.
</p>
<p>Invite Your Friends to Play<br >
One of the best ways to connect and play with others is to invite your friends from the “real world” to join you on JamKazam, and then set up a time to meet online and get into a session together. To do this, just go to www.jamkazam.com and sign in. Then move your mouse over your picture or name in the upper right corner of the screen. A menu will be displayed. Click the Invite Friends option, and then you can click Facebook, Google, or Email to easily invite your friends from different services to join you on JamKazam.
</p>
<p>If you have any trouble, please visit our support center at the link below any time to get help:
</p>
<p><a style="color: #588C98;" href="https://jamkazam.desk.com/">https://jamkazam.desk.com</a>
</p>
<p>-- Team JamKazam
</p>

View File

@ -0,0 +1,20 @@
<% provide(:title, @title) %>
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
We noticed that you havent yet connected with any friends on JamKazam. Connecting with friends is the best way to help you get into sessions with other musicians on JamKazam. Here are a couple of good ways to connect with others.
Find Other Musicians on JamKazam
To find and connect with other musicians who are already on JamKazam, wed suggest you click the Musicians tile on the home screen of the app or the website: http://www.jamkazam.com/client#/musicians
This will display the JamKazam musicians sorted by latency to you - in other words, you can see which musicians have good network connections to you. Any musicians with green and yellow latency scores have good enough connections to support a play session with you. We recommend that you read the profiles of these musicians to find others with shared musical interests and good network connections to you, and then use the Message button to say hi and see if they are interested in playing with you. If they are, use the Connect button to “friend” them on JamKazam, and use the Message button to set up a time to meet online for a session.
Invite Your Friends to Play
One of the best ways to connect and play with others is to invite your friends from the “real world” to join you on JamKazam, and then set up a time to meet online and get into a session together. To do this, just go to www.jamkazam.com and sign in. Then move your mouse over your picture or name in the upper right corner of the screen. A menu will be displayed. Click the Invite Friends option, and then you can click Facebook, Google, or Email to easily invite your friends from different services to join you on JamKazam.
If you have any trouble, please visit our support center at the link below any time to get help:
https://jamkazam.desk.com
-- Team JamKazam

View File

@ -0,0 +1,16 @@
<% provide(:title, @title) %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<p>We noticed that you have not invited any of your friends to join you to play together on JamKazam. Its still very early in our companys development, so we dont have zillions of users online on our service yet. One of the best ways to connect and play with others is to invite your friends from the “real world” to join you on JamKazam, and then set up a time to meet online and get into a session together. To do this, just go to www.jamkazam.com and sign in. Then move your mouse over your picture or name in the upper right corner of the screen. A menu will be displayed. Click the Invite Friends option, and then you can click Facebook, Google, or Email to easily invite your friends from different services to join you on JamKazam.
</p>
<p>If you have any trouble, please visit our support center at the link below any time to get help:
</p>
<p><a style="color: #588C98;" href="https://jamkazam.desk.com">https://jamkazam.desk.com</a>
</p>
<p>-- Team JamKazam
</p>

View File

@ -0,0 +1,11 @@
<% provide(:title, @title) %>
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
We noticed that you have not invited any of your friends to join you to play together on JamKazam. Its still very early in our companys development, so we dont have zillions of users online on our service yet. One of the best ways to connect and play with others is to invite your friends from the “real world” to join you on JamKazam, and then set up a time to meet online and get into a session together. To do this, just go to www.jamkazam.com and sign in. Then move your mouse over your picture or name in the upper right corner of the screen. A menu will be displayed. Click the Invite Friends option, and then you can click Facebook, Google, or Email to easily invite your friends from different services to join you on JamKazam.
If you have any trouble, please visit our support center at the link below any time to get help:
https://jamkazam.desk.com
-- Team JamKazam

View File

@ -0,0 +1,14 @@
<% provide(:title, @title) %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<p>JamKazam is a young company/service built through the sweat and commitment of a small group of music-loving techies. Please help us continue to grow the service and attract more musicians to play online by liking and/or following us on Facebook, Twitter, and Google+. Just click the icons below to give us little push, thanks!
</p>
<% [:twitter, :facebook, :google].each do |site| %>
<%= link_to(image_tag("http://www.jamkazam.com/assets/content/icon_#{site}.png", :style => "vertical-align:top"), "http://www.jamkazam.com/endorse/@USERID/#{site}?src=email") %>&nbsp;
<% end %>
<p>-- Team JamKazam
</p>

View File

@ -0,0 +1,12 @@
<% provide(:title, @title) %>
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
JamKazam is a young company/service built through the sweat and commitment of a small group of music-loving techies. Please help us continue to grow the service and attract more musicians to play online by liking and/or following us on Facebook, Twitter, and Google+. Just click the icons below to give us little push, thanks!
<% [:twitter, :facebook, :google].each do |site| %>
http://www.jamkazam.com/endorse/@USERID/#{site}?src=email
<% end %>
-- Team JamKazam

View File

@ -0,0 +1,19 @@
<% provide(:title, @title) %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<p>We noticed that you havent yet rated any of your JamKazam sessions as “good”. It may be that you just are not a “rater”, and that is totally fine. But if you are not having good, high quality, productive and fun sessions, we want to help you get there!
</p>
<p>If you are having audio quality problems or other issues when you get into a session, please click the link below to visit our support center, and check the knowledge base articles under the Troubleshooting header to find solutions. And if that doesnt work, please post a request for assistance in the support center so that we can help you get up and running:
</p>
<p><a style="color: #588C98;" href="https://jamkazam.desk.com">https://jamkazam.desk.com</a>
</p>
<p>We really want you to be successful and have fun with this new way of playing music with others, so please reach out and let us help you!
</p>
<p>-- Team JamKazam
</p>

View File

@ -0,0 +1,13 @@
<% provide(:title, @title) %>
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
We noticed that you havent yet rated any of your JamKazam sessions as “good”. It may be that you just are not a “rater”, and that is totally fine. But if you are not having good, high quality, productive and fun sessions, we want to help you get there!
If you are having audio quality problems or other issues when you get into a session, please click the link below to visit our support center, and check the knowledge base articles under the Troubleshooting header to find solutions. And if that doesnt work, please post a request for assistance in the support center so that we can help you get up and running:
https://jamkazam.desk.com
We really want you to be successful and have fun with this new way of playing music with others, so please reach out and let us help you!
-- Team JamKazam

View File

@ -0,0 +1,19 @@
<% provide(:title, @title) %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<p>We noticed that you havent yet made a recording during any of your JamKazam sessions. Recordings are extra special on JamKazam because:
</p>
<ul><li>Recordings are made both as a master mix and at the track/stem level.</li>
<li>You can easily play along with your recordings when your friends arent available.</li>
<li>You can share your recordings with family, friends and fans via Facebook, Twitter, etc.</li>
</ul>
<!--<p>To understand all of the things you can do with recordings on JamKazam, watch this YouTube tutorial video:<br />
www.youtube.com/blahblahblah
</p>-->
<p>-- Team JamKazam
</p>

View File

@ -0,0 +1,11 @@
<% provide(:title, @title) %>
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
We noticed that you havent yet made a recording during any of your JamKazam sessions. Recordings are extra special on JamKazam because:
- Recordings are made both as a master mix and at the track/stem level.
- You can easily play along with your recordings when your friends arent available.
- You can share your recordings with family, friends and fans via Facebook, Twitter, etc.
-- Team JamKazam

View File

@ -1,4 +1,8 @@
<% provide(:title, 'New JamKazam Musicians in your Area') %>
Hi <%= @user.first_name %>,
<p>The following new musicians have joined JamKazam within the last week, and have Internet connections with low enough latency to you that you can have a good online session together. We'd suggest that you look through the new musicians listed below to see if any match your musical interests, and if so, click through to their profile page on the JamKazam website to send them a message or a request to connect as a JamKazam friend:
</p>
<% link_style = "background-color:#ED3618; margin:0px 8px 0px 8px; border: solid 1px #F27861; outline: solid 2px #ED3618; padding:3px 10px; font-family:Raleway, Arial, Helvetica, sans-serif; font-size:12px; font-weight:300; cursor:pointer; color:#FC9; text-decoration:none;" %>
<p>
@ -20,3 +24,8 @@
<% end %>
</table>
</p>
<p>There are currently <%= @new_nearby.size%> musicians on JamKazam with low enough latency Internet connections to you to support a good online session. To see ALL the JamKazam musicians with whom you may want to connect and play, view our Musicians page at: <a href="http://www.jamkazam.com/client#/musicians">http://www.jamkazam.com/client#/musicians</a>.
</p>
<p>Best Regards,</p>
Team JamKazam

View File

@ -1,9 +1,17 @@
New JamKazam Musicians in your Area
Hi <%= @user.first_name %>,
The following new musicians have joined JamKazam within the last week, and have Internet connections with low enough latency to you that you can have a good online session together. We'd suggest that you look through the new musicians listed below to see if any match your musical interests, and if so, click through to their profile page on the JamKazam website to send them a message or a request to connect as a JamKazam friend:
<% @new_nearby.each do |user| %>
<%= user.name %> (http://<%= @host %>/client#/profile/<%= user.id %>)
<%= user.location %>
<% user.instruments.collect { |inst| inst.description }.join(', ') %>
<%= user.biography %>
<% end %>
There are currently <%= @new_nearby.size%> musicians on JamKazam with low enough latency Internet connections to you to support a good online session. To see ALL the JamKazam musicians with whom you may want to connect and play, view our Musicians page at: http://www.jamkazam.com/client#/musicians.
Best Regards,
Team JamKazam

View File

@ -1,27 +1,45 @@
<% provide(:title, 'Welcome to JamKazam!') %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<p> We're delighted that you have decided to try the JamKazam service,
and we hope that you will enjoy using JamKazam to play music with others.
Following are links to some resources that can help to get you up and running quickly.
</p>
<p>
Tutorial videos that show you how to use the key features of the product:</br>
Getting Started Video<br/>
We recommend watching this video before you jump into the service just to get oriented. It will really help you hit the ground running:
<a style="color: #588C98;" href="https://www.youtube.com/watch?v=VexH4834o9I">https://www.youtube.com/watch?v=VexH4834o9I</a>
</p>
<p>
Other Great Tutorial Videos<br />
There are several other very great videos that will help you understand how to find and connect with other musicians on the service, create your own sessions or find and join other musicians sessions, play in sessions, record and share your performances, and even live broadcast your sessions to family, friends, and fans. Check these helpful videos out here:
<a style="color: #588C98;" href="https://jamkazam.desk.com/customer/portal/articles/1304097-tutorial-videos">https://jamkazam.desk.com/customer/portal/articles/1304097-tutorial-videos</a>
</p>
<p>
Getting Started knowledge base articles:</br>
Knowledge Base Articles<br />
You can find Getting Started knowledge base articles on things like frequently asked questions (FAQ), minimum system requirements for your Windows or Mac computer, how to troubleshoot audio problems in sessions, and more here:
<a style="color: #588C98;" href="https://jamkazam.desk.com/customer/portal/topics/564807-getting-started/articles">https://jamkazam.desk.com/customer/portal/topics/564807-getting-started/articles</a>
</p>
<p>
Support Portal in case you run into trouble and need help:</br>
JamKazam Support Portal<br />
If you run into trouble and need help, please reach out to us. We will be glad to do everything we can to get you up and running. You can find our support portal here:
<a style="color: #588C98;" href="https://jamkazam.desk.com/">https://jamkazam.desk.com/</a>
</p>
<p>
We look forward to seeing - and hearing - you online soon!
JamKazam Community Forum<br />
And if you just want to chat, share tips and war stories, and hang out with fellow JamKazamers, you can visit our community forum here:
<a style="color: #588C98;" href="http://forums.jamkazam.com/">http://forums.jamkazam.com/</a>
</p>
&nbsp;&nbsp;- Team JamKazam
<p>
Please take a moment to like or follow us by clicking the icons below, and we look forward to seeing and hearing you online soon!
</p>
&nbsp;&nbsp;-- Team JamKazam

View File

@ -1,16 +1,27 @@
We're delighted that you have decided to try the JamKazam service,
and we hope that you will enjoy using JamKazam to play music with others.
Following are links to some resources that can help to get you up and running quickly.
Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
Tutorial videos that show you how to use the key features of the product:
We're delighted that you have decided to try the JamKazam service, and we hope that you will enjoy using JamKazam to play music with others. Following are links to some resources that can help to get you up and running quickly.
Getting Started Video
We recommend watching this video before you jump into the service just to get oriented. It will really help you hit the ground running:
https://www.youtube.com/watch?v=VexH4834o9I
Other Great Tutorial Videos
There are several other very great videos that will help you understand how to find and connect with other musicians on the service, create your own sessions or find and join other musicians sessions, play in sessions, record and share your performances, and even live broadcast your sessions to family, friends, and fans. Check these helpful videos out here:
https://jamkazam.desk.com/customer/portal/articles/1304097-tutorial-videos
Getting Started knowledge base articles:
Knowledge Base Articles
You can find Getting Started knowledge base articles on things like frequently asked questions (FAQ), minimum system requirements for your Windows or Mac computer, how to troubleshoot audio problems in sessions, and more here:
https://jamkazam.desk.com/customer/portal/topics/564807-getting-started/articles
Support Portal in case you run into trouble and need help:
https://jamkazam.desk.com/
JamKazam Support Portal
If you run into trouble and need help, please reach out to us. We will be glad to do everything we can to get you up and running. You can find our support portal here:
https://jamkazam.desk.com
We look forward to seeing - and hearing - you online soon!
JamKazam Community Forum
And if you just want to chat, share tips and war stories, and hang out with fellow JamKazamers, you can visit our community forum here:
http://forums.jamkazam.com
- Team JamKazam
Please take a moment to like or follow us by clicking the icons below, and we look forward to seeing and hearing you online soon!
-- Team JamKazam

View File

@ -49,8 +49,8 @@
<table align="center" width="650" cellpadding="10" bgcolor="#156572" cellspacing="0">
<tr>
<td align="center" valign="top">
<% [:twitter, :facebook, :google].each do |src| %>
<%= link_to(image_tag("http://www.jamkazam.com/assets/content/icon_#{src}.png", :style => "vertical-align:top"), "http://www.jamkazam.com/endorse/@USERID/#{src}") %>&nbsp;
<% [:twitter, :facebook, :google].each do |site| %>
<%= link_to(image_tag("http://www.jamkazam.com/assets/content/icon_#{site}.png", :style => "vertical-align:top"), "http://www.jamkazam.com/endorse/@USERID/#{site}?src=email") %>&nbsp;
<% end %>
</td>
</tr>

View File

@ -347,7 +347,7 @@ SQL
connection = Connection.find_by_client_id_and_user_id!(client_id, user.id)
connection.join_the_session(music_session, as_musician, tracks)
connection.join_the_session(music_session, as_musician, tracks, user)
# connection.music_session_id = music_session.id
# connection.as_musician = as_musician
# connection.joining_session = true

View File

@ -27,7 +27,10 @@ module JamRuby
start = params[:start].presence
start = start.to_i || 0
query = ChatMessage.offset(start).limit(limit)
music_session_id = params[:music_session]
query = ChatMessage.where('music_session_id = ?', music_session_id)
.offset(start).limit(limit)
if query.length == 0
[query, nil]

View File

@ -134,7 +134,7 @@ module JamRuby
end
def did_create
self.user.update_lat_lng(self.ip_address) if self.user && self.ip_address
# self.user.update_lat_lng(self.ip_address) if self.user && self.ip_address
end
def report_add_participant
@ -148,13 +148,18 @@ module JamRuby
true
end
def join_the_session(music_session, as_musician, tracks)
def join_the_session(music_session, as_musician, tracks, user)
self.music_session_id = music_session.id
self.as_musician = as_musician
self.joining_session = true
self.joined_session_at = Time.now
associate_tracks(tracks) unless tracks.nil?
self.save
# if user joins the session as a musician, update their addr and location
if as_musician
user.update_addr_loc(self, 'j')
end
end
def associate_tracks(tracks)

View File

@ -13,7 +13,7 @@ module JamRuby
VAR_LAST_NAME = '@LASTNAME'
DEFAULT_SENDER = "noreply@jamkazam.com"
BATCH_SIZE = 5
BATCH_SIZE = 500
BODY_TEMPLATE =<<FOO
Hello #{VAR_FIRST_NAME},
@ -78,11 +78,20 @@ FOO
self.test_emails.present? && (self.tested? || self.pending?)
end
def deliver_batch_sets!
User.email_opt_in.find_in_batches(batch_size: BATCH_SIZE) do |users|
self.email_batch_sets << (bset = EmailBatchSet.load_set(self, users.map(&:id)))
if 'test' == Rails.env
BatchMailer.send_batch_email(self.id, bset.user_ids).deliver!
else
BatchMailer.send_batch_email(self.id, bset.user_ids).deliver
end
end
end
def deliver_batch
self.perform_event('do_batch_run!')
User.email_opt_in.find_in_batches(batch_size: BATCH_SIZE) do |users|
self.email_batch_sets << EmailBatchSet.deliver_set(self.id, users.map(&:id))
end
self.deliver_batch_sets!
end
def test_count
@ -195,5 +204,13 @@ FOO
})
end
def batch_substitutions(users=[])
{}
end
def body_for_users(users=[])
self.body
end
end
end

View File

@ -0,0 +1,48 @@
module JamRuby
class EmailBatchNewMusician < EmailBatchPeriodic
BATCH_SIZE = 500
SINCE_WEEKS = 2
VAR_MUSICIAN_COUNT = "@MUSICIAN_COUNT"
VAR_MUSICIAN_TABLE = "@MUSICIAN_TABLE"
def self.subject
"New musicians with good Internet connections to you have joined JamKazam!"
end
def fetch_recipients(since=nil)
since ||= Time.now - SINCE_WEEKS.weeks
User.geocoded_users
.email_opt_in
.where(['created_at < ?', since])
.find_in_batches(batch_size: EmailBatchNewMusician::BATCH_SIZE) do |users|
new_musicians = users.inject({}) do |hh, uu|
if 0 < (nearby = uu.nearest_musicians).count
hh[uu] = nearby
end
hh
end
yield(new_musicians) if block_given?
end
end
def deliver_batch_sets!
self.opt_in_count = 0
sent = 0
self.fetch_recipients(self.time_since_last_batch(SINCE_WEEKS)) do |new_musicians|
self.opt_in_count += new_musicians.count
self.email_batch_sets << (bset = EmailBatchSet.load_set(self, new_musicians.keys.map(&:id)))
new_musicians.each do |uu, nearby|
UserMailer.new_musicians(uu, nearby).deliver
sent += 1
end
end
self.sent_count = sent
self.save
self.did_batch_run!
end
end
end

View File

@ -0,0 +1,46 @@
module JamRuby
class EmailBatchPeriodic < EmailBatch
self.abstract_class = true
def time_since_last_batch_query
self.class
.where(['created_at < ?', self.created_at])
.order('created_at DESC')
.limit(1)
end
def time_since_last_batch(default_weeks=2)
if previous = self.time_since_last_batch_query.first
return previous.created_at
end
Time.now - default_weeks.weeks
end
def fetch_recipients(since=nil)
yield([]) if block_given?
end
def self.subject(subtype=nil)
''
end
def self.body
''
end
def self.new(*args)
oo = super
oo.body = ''
oo.subject = self.subject
oo
end
def deliver_batch_sets!
end
def clear_batch_sets!
self.email_batch_sets.map(&:destroy)
end
end
end

View File

@ -0,0 +1,227 @@
# -*- coding: utf-8 -*-
module JamRuby
class EmailBatchProgression < EmailBatchPeriodic
BATCH_SIZE = 500
SINCE_WEEKS = 2
SUBTYPES = [:client_notdl, # Registered Musician Has Not Downloaded Client
:client_dl_notrun, # Registered Musician Has Downloaded Client But Not Yet Run It
:client_run_notgear, # Registered Musician Has Run Client But Not Successfully Qualified Audio Gear
:gear_notsess, # Registered Musician Has Successfully Qualified Audio Gear But Has Not Participated in a Real Session
:sess_notgood, # Registered Musician Has Participated In a "Real" Session But Has Not Had a "Good" Session
:sess_notrecord, # Registered Musician Has Participated In a "Real" Session But Has Not Made a Recording
:reg_notinvite, # Registered Musician Has Not Invited Friends to Join JamKazam
:reg_notconnect, # Registered Musician Has Not Connected with any Friends on JamKazam
:reg_notlike, # Registered Musician Has Not Liked Jamkazam
]
SUBTYPE_METADATA = {
:client_notdl => {
:subject => "Get the free JamKazam app now and start playing with others!",
:title => "Download the Free JamKazam App"
},
:client_dl_notrun => {
:subject => "Having trouble running the JamKazam application?",
:title => "Running the JamKazam App"
},
:client_run_notgear => {
:subject => "Having trouble setting up your audio gear for JamKazam?",
:title => "Setting Up and Qualifying Your Audio Gear on JamKazam"
},
:gear_notsess => {
:subject => "Having trouble getting into a session with other musicians?",
:title => "Tips on Getting into Sessions with Other Musicians"
},
:sess_notgood => {
:subject => "Have you played in a “good” session on JamKazam yet?",
:title => "Having a Good Session on JamKazam"
},
:reg_notinvite => {
:subject => "Invite your friends to JamKazam, best way to play online!",
:title => "Invite Your Friends to Come and Play with You"
},
:reg_notconnect => {
:subject => "Make friends on JamKazam and play more music!",
:title => "Connecting with Friends on JamKazam"
},
:reg_notlike => {
:subject => "Please give us a LIKE!",
:title => "Like/Follow JamKazam"
},
:sess_notrecord => {
:subject => "Want to make recordings during your JamKazam sessions?",
:title => "Making & Sharing Recordings on JamKazam"
},
}
def self.subject(subtype=nil)
SUBTYPE_METADATA[subtype][:subject] if subtype
end
def self.title(subtype=nil)
SUBTYPE_METADATA[subtype][:title] if subtype
end
def self.subtype_trigger_interval(subtype)
[:reg_notlike, :sess_notrecord].include?(subtype) ? [7,14,21] : [2,5,14]
end
def self.days_past_for_trigger_index(subtype, idx)
self.subtype_trigger_interval(subtype)[idx]
end
def days_diff(tidx)
self.class.days_past_for_trigger_index(self.sub_type,tidx) - self.class.days_past_for_trigger_index(self.sub_type, tidx - 1)
end
def days_past_for_trigger_index(idx)
self.class.subtype_trigger_interval(self.sub_type)[idx]
end
def make_set(uu, trigger_idx)
EmailBatchSet.progress_set(self, uu, trigger_idx)
end
def trigger_date_constraint(trigger_idx, tbl='tt')
intervals = self.class.subtype_trigger_interval(self.sub_type)
date_constraint = (Time.now - intervals[trigger_idx].days + 1.hour).strftime('%Y-%m-%d %H:%M:%S')
case self.sub_type.to_sym
when :client_notdl, :reg_notconnect, :reg_notinvite, :reg_notlike
return "#{tbl}.created_at < '#{date_constraint}'"
when :client_dl_notrun
return "#{tbl}.first_downloaded_client_at < '#{date_constraint}'"
when :client_run_notgear
return "#{tbl}.first_ran_client_at < '#{date_constraint}'"
when :gear_notsess
return "#{tbl}.first_certified_gear_at < '#{date_constraint}'"
when :sess_notgood
return "#{tbl}.first_real_music_session_at < '#{date_constraint}'"
when :sess_notrecord
return "#{tbl}.first_real_music_session_at < '#{date_constraint}'"
end
end
def progress_column_constraint(tbl='tt')
case self.sub_type.to_sym
when :client_notdl
return "#{tbl}.first_downloaded_client_at IS NULL"
when :client_dl_notrun
return "#{tbl}.first_downloaded_client_at IS NOT NULL AND #{tbl}.first_ran_client_at IS NULL"
when :client_run_notgear
return "#{tbl}.first_ran_client_at IS NOT NULL AND #{tbl}.first_certified_gear_at IS NULL"
when :gear_notsess
return "#{tbl}.first_certified_gear_at IS NOT NULL AND #{tbl}.first_real_music_session_at IS NULL"
when :sess_notgood
return "#{tbl}.first_real_music_session_at IS NOT NULL AND #{tbl}.first_good_music_session_at IS NULL"
when :sess_notrecord
return "#{tbl}.first_real_music_session_at IS NOT NULL AND #{tbl}.first_recording_at IS NULL"
when :reg_notinvite
return "#{tbl}.first_invited_at IS NULL"
when :reg_notconnect
return "#{tbl}.first_friended_at IS NULL"
when :reg_notlike
return "#{tbl}.first_social_promoted_at IS NULL"
end
''
end
def fetch_recipients(trigger_idx=0, per_page=BATCH_SIZE)
fetched = []
offset = 0
if 0==trigger_idx
prev_date_sql = 'tt.prev_trigger_date IS NULL'
else
prev_date_sql = "tt.prev_trigger_date + interval '#{self.days_diff(trigger_idx)} days' <= '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}'"
end
countsql =<<SQL
SELECT COUNT(*)
FROM (SELECT "users".*,
(SELECT COALESCE(MAX(ebs.trigger_index),-1)
FROM email_batch_sets ebs
WHERE
ebs.sub_type = '#{self.sub_type}' AND
ebs.user_id = users.id
) AS tidx,
(SELECT created_at
FROM email_batch_sets ebs
WHERE
ebs.sub_type = '#{self.sub_type}' AND
ebs.user_id = users.id AND trigger_index = #{trigger_idx - 1}
) AS prev_trigger_date
FROM users) tt
WHERE
tt."subscribe_email" = 't' AND
tt."musician" = 't' AND
(#{progress_column_constraint}) AND
(#{self.trigger_date_constraint(trigger_idx)}) AND
tt.tidx = #{trigger_idx - 1} AND
#{prev_date_sql}
SQL
num_users = User.count_by_sql(countsql)
loops = (num_users / per_page) + (num_users % per_page) - 1
0.upto(loops) do |nn|
offset = nn * per_page
sql =<<SQL
SELECT tt.*
FROM (SELECT "users".*,
(SELECT COALESCE(MAX(ebs.trigger_index),-1)
FROM email_batch_sets ebs
WHERE
ebs.sub_type = '#{self.sub_type}' AND
ebs.user_id = users.id
) AS tidx,
(SELECT created_at
FROM email_batch_sets ebs
WHERE
ebs.sub_type = '#{self.sub_type}' AND
ebs.user_id = users.id AND trigger_index = #{trigger_idx - 1}
) AS prev_trigger_date
FROM users) tt
WHERE
tt."subscribe_email" = 't' AND
tt."musician" = 't' AND
(#{progress_column_constraint}) AND
(#{self.trigger_date_constraint(trigger_idx)}) AND
tt.tidx = #{trigger_idx - 1} AND
#{prev_date_sql}
ORDER BY tt.id
ASC LIMIT #{per_page}
OFFSET #{offset}
SQL
users = User.find_by_sql(sql)
block_given? ? yield(users) : fetched.concat(users)
end
fetched
end
def deliver_batch_sets!
self.opt_in_count = 0
sent = 0
3.times do |trigger_idx|
self.fetch_recipients(trigger_idx) do |users|
users.each do |uu|
self.email_batch_sets << (bset = self.make_set(uu, trigger_idx))
ProgressMailer.send_reminder(bset).deliver
sent += 1
end
end
end
self.sent_count = sent
self.save
self.did_batch_run!
end
def self.send_progress_batch
self::SUBTYPES.collect do |stype|
ebatch = self.create
ebatch.update_attribute(:sub_type, stype.to_s)
ebatch
end.each do |ebatch|
ebatch.deliver_batch
end
end
end
end

View File

@ -3,21 +3,53 @@ module JamRuby
self.table_name = "email_batch_sets"
belongs_to :email_batch, :class_name => 'JamRuby::EmailBatch'
belongs_to :user, :class_name => 'JamRuby::User'
def self.deliver_set(batch_id, user_ids)
def self.load_set(batch, user_ids)
bset = self.new
bset.email_batch_id = batch_id
bset.email_batch_id = batch.id
bset.user_ids = user_ids.join(',')
bset.started_at = Time.now
bset.batch_count = user_ids.size
bset.save!
if 'test' == Rails.env
BatchMailer.send_batch_email(bset.email_batch_id, user_ids).deliver!
else
BatchMailer.send_batch_email(bset.email_batch_id, user_ids).deliver
end
bset
end
def self.progress_set(batch, user, trigger_idx)
bset = self.new
bset.email_batch = batch
bset.user = user
bset.started_at = Time.now
bset.batch_count = 1
bset.trigger_index = trigger_idx
bset.sub_type = batch.sub_type
bset.save!
bset
end
def subject
unless sub_type.blank?
return EmailBatchProgression.subject(self.sub_type.to_sym)
end
''
end
def title
unless sub_type.blank?
return EmailBatchProgression.title(self.sub_type.to_sym)
end
''
end
def previous_trigger_date
return nil if 0 == self.trigger_index.to_i || self.user_id.nil?
self.class
.where(['email_batch__id = ? AND user_id = ? AND sub_type = ? AND trigger_index = ?',
self.email_batch_id, self.user_id, self.sub_type, self.trigger_index - 1])
.pluck(:created_at)
.limit(1)
.first
end
end
end

View File

@ -3,15 +3,15 @@ module JamRuby
self.table_name = "connections"
def self.get_work(mylocidispid)
list = self.get_work_list(mylocidispid)
def self.get_work(mylocidispid, myaddr)
list = self.get_work_list(mylocidispid, myaddr)
return nil if list.nil?
return nil if list.length == 0
return list[0]
end
def self.get_work_list(mylocidispid)
r = GetWork.select(:client_id).find_by_sql("select get_work(#{mylocidispid}) as client_id")
def self.get_work_list(mylocidispid, myaddr)
r = GetWork.select(:client_id).find_by_sql("select get_work(#{mylocidispid}, #{myaddr}) as client_id")
#puts("r = #{r}")
a = r.map {|i| i.client_id}
#puts("a = #{a}")

View File

@ -88,35 +88,42 @@ module JamRuby
end
end
end
User.find_each { |usr| usr.update_lat_lng }
# 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
# this is only valid to call when relation is about bands
distance = params[:distance].to_i
if distance > 0
latlng = nil
location_city = params[:city]
location_state = params[:state]
location_country = params[:country]
remote_ip = params[:remote_ip]
if location_city and location_state and location_country
geo = self.where(city: location_city, region: location_state, country: location_country).limit(1).first
if geo and geo.lat and geo.lng and (geo.lat != 0 or geo.lng != 0)
# it isn't reasonable for both to be 0...
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]))
geo.lat = nil if geo.lat = 0
geo.lng = nil if geo.lng = 0
latlng = [geo.lat, geo.lng] if geo.lat && geo.lng
end
else
latlng = [current_user.lat, current_user.lng]
elsif current_user and current_user.locidispid and current_user.locidispid != 0
location = GeoIpLocations.lookup(current_user.locidispid/1000000)
if location and location.latitude and location.longitude and (location.latitude != 0 or location.longitude != 0)
# it isn't reasonable for both to be 0...
latlng = [location.latitude, location.longitude]
end
elsif remote_ip
geo = self.ip_lookup(remote_ip)
if geo and geo.lat and geo.lng and (geo.lat != 0 or geo.lng != 0)
# it isn't reasonable for both to be 0...
latlng = [geo.lat, geo.lng]
end
elsif params[:remote_ip] && (geo = self.ip_lookup(params[:remote_ip]))
geo.lat = nil if geo.lat = 0
geo.lng = nil if geo.lng = 0
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)
if latlng
relation = relation.where(['lat IS NOT NULL AND lng IS NOT NULL']).within(distance, origin: latlng)
end
end
relation

View File

@ -7,47 +7,50 @@ module JamRuby
self.where(countrycode: country).order('regionname asc').all
end
def self.import_from_xx_region(countrycode, file)
def self.import_from_region_codes(file)
# File xx_region.csv
# File region_codes.csv
# Format:
# region,regionname
# countrycode,region,regionname
# what this does is not replace the contents of the table, but rather update the specifies rows with the names.
# any rows not specified are left alone. the parameter countrycode denote the country of the region (when uppercased)
raise "countrycode (#{MaxMindIsp.quote_value(countrycode)}) is missing or invalid (it must be two characters)" unless countrycode and countrycode.length == 2
countrycode = countrycode.upcase
# what this does is replace the contents of the table with the new data.
self.transaction do
self.connection.execute "update #{self.table_name} set regionname = region where countrycode = #{MaxMindIsp.quote_value(countrycode)}"
self.connection.execute "delete from #{self.table_name}"
File.open(file, 'r:ISO-8859-1') do |io|
saved_level = ActiveRecord::Base.logger ? ActiveRecord::Base.logger.level : 0
saved_level = ActiveRecord::Base.logger ? ActiveRecord::Base.logger.level : -1
count = 0
ncols = 2
errors = 0
ncols = 3
csv = ::CSV.new(io, {encoding: 'ISO-8859-1', headers: false})
csv.each do |row|
raise "file does not have expected number of columns (#{ncols}): #{row.length}" unless row.length == ncols
region = row[0]
regionname = row[1]
countrycode = row[0]
region = row[1]
regionname = row[2]
stmt = "UPDATE #{self.table_name} SET regionname = #{MaxMindIsp.quote_value(regionname)} WHERE countrycode = #{MaxMindIsp.quote_value(countrycode)} AND region = #{MaxMindIsp.quote_value(region)}"
self.connection.execute stmt
count += 1
if countrycode.length == 2 and region.length == 2 and regionname.length >= 2 and regionname.length <= 64
if ActiveRecord::Base.logger and ActiveRecord::Base.logger.level < Logger::INFO
ActiveRecord::Base.logger.debug "... logging updates to #{self.table_name} suspended ..."
ActiveRecord::Base.logger.level = Logger::INFO
stmt = "INSERT INTO #{self.table_name} (countrycode, region, regionname) VALUES (#{self.connection.quote(countrycode)}, #{self.connection.quote(region)}, #{self.connection.quote(regionname)})"
self.connection.execute stmt
count += 1
if ActiveRecord::Base.logger and ActiveRecord::Base.logger.level < Logger::INFO
ActiveRecord::Base.logger.debug "... logging updates to #{self.table_name} suspended ..."
ActiveRecord::Base.logger.level = Logger::INFO
end
else
ActiveRecord::Base.logger.warn("bogus region_codes record '#{countrycode}', '#{region}', '#{regionname}'") if ActiveRecord::Base.logger
errors += 1
end
end
if ActiveRecord::Base.logger
ActiveRecord::Base.logger.level = saved_level
ActiveRecord::Base.logger.debug "updated #{count} records in #{self.table_name}"
ActiveRecord::Base.logger.debug "inserted #{count} records into #{self.table_name}, #{errors} errors"
end
end # file
end # transaction

View File

@ -5,13 +5,15 @@ module JamRuby
self.table_name = 'scores'
attr_accessible :alocidispid, :anodeid, :aaddr, :blocidispid, :bnodeid, :baddr, :score, :score_dt, :scorer
attr_accessible :alocidispid, :anodeid, :aaddr, :blocidispid, :bnodeid, :baddr, :score, :score_dt, :scorer, :scoring_data
default_scope order('score_dt desc')
def self.createx(alocidispid, anodeid, aaddr, blocidispid, bnodeid, baddr, score, score_dt)
def self.createx(alocidispid, anodeid, aaddr, blocidispid, bnodeid, baddr, score, score_dt=nil, score_data=nil)
score_dt = Time.new.utc if score_dt.nil?
Score.create(alocidispid: alocidispid, anodeid: anodeid, aaddr: aaddr, blocidispid: blocidispid, bnodeid: bnodeid, baddr: baddr, score: score, scorer: 0, score_dt: score_dt)
score = score.ceil
raise "score must be positive" if score <= 0
Score.create(alocidispid: alocidispid, anodeid: anodeid, aaddr: aaddr, blocidispid: blocidispid, bnodeid: bnodeid, baddr: baddr, score: score, scorer: 0, score_dt: score_dt, scoring_data: score_data)
Score.create(alocidispid: blocidispid, anodeid: bnodeid, aaddr: baddr, blocidispid: alocidispid, bnodeid: anodeid, baddr: aaddr, score: score, scorer: 1, score_dt: score_dt) if alocidispid != blocidispid
end
@ -25,5 +27,9 @@ module JamRuby
return -1 if s.nil?
return s.score
end
def self.score_conns(c1, c2, score)
self.createx(c1.locidispid, c1.client_id, c1.addr, c2.locidispid, c2.client_id, c2.addr, score)
end
end
end

View File

@ -120,44 +120,95 @@ module JamRuby
ordering.blank? ? keys[0] : keys.detect { |oo| oo.to_s == ordering }
end
def self.musician_filter(params={}, current_user=nil)
rel = User.musicians
# produce a list of musicians (users where musician is true)
# params:
# instrument - instrument to search for or blank
# score_limit - score must be <= this to be included in the result
# handled by relation_pagination:
# page - page number to fetch (origin 1)
# per_page - number of entries per page
# handled by order_param:
# orderby - ??? (followed, plays, playing)
# handled by where_latlng:
# distance - defunct
# city - defunct
# remote_ip - defunct
def self.musician_filter(params={}, user=nil, conn=nil)
# puts "================ params #{params.inspect}"
# puts "================ user #{user.inspect}"
# puts "================ conn #{conn.inspect}"
rel = User.musicians_geocoded
rel = rel.select('users.*')
rel = rel.group('users.id')
unless (instrument = params[:instrument]).blank?
rel = rel.joins("RIGHT JOIN musicians_instruments AS minst ON minst.user_id = users.id")
.where(['minst.instrument_id = ? AND users.id IS NOT NULL', instrument])
rel = rel.joins("inner JOIN musicians_instruments AS minst ON minst.user_id = users.id")
.where(['minst.instrument_id = ?', instrument])
end
rel = MaxMindGeo.where_latlng(rel, params, current_user)
# to find appropriate musicians we need to join users with scores to get to those with no scores or bad scores
# weeded out
sel_str = 'users.*'
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")
.joins("LEFT JOIN recordings AS records ON records.owner_id = users.id")
.group("users.id")
.order("play_count DESC, users.created_at DESC")
when :followed
sel_str = "COUNT(follows) AS search_follow_count, #{sel_str}"
rel = rel.joins("LEFT JOIN follows ON follows.followable_id = users.id")
.group("users.id")
.order("COUNT(follows) DESC, users.created_at DESC")
when :playing
rel = rel.joins("LEFT JOIN connections ON connections.user_id = users.id")
.where(['connections.music_session_id IS NOT NULL AND connections.aasm_state != ?',
'expired'])
.order("users.created_at DESC")
# filter on scores using selections from params
# todo scott what is the real limit?
score_limit = 60
l = params[:score_limit]
unless l.nil? or l.to_i <= 0
score_limit = l.to_i
end
rel = rel.select(sel_str)
# puts "================ score_limit #{score_limit}"
locidispid = ((conn and conn.client_type == 'client') ? conn.locidispid : ((user and user.musician) ? user.last_jam_locidispid : nil))
# puts "================ locidispid #{locidispid}"
unless locidispid.nil?
rel = rel.joins('inner join scores on scores.alocidispid = users.last_jam_locidispid')
.where(['scores.blocidispid = ?', locidispid])
.where(['scores.score <= ?', score_limit])
rel = rel.select('scores.score')
rel = rel.group('scores.score')
end
ordering = self.order_param(params)
# puts "================ ordering #{ordering}"
case ordering
when :plays # FIXME: double counting?
# sel_str = "COUNT(records)+COUNT(sessions) AS play_count, #{sel_str}"
rel = rel.select('COUNT(records.id)+COUNT(sessions.id) AS play_count')
rel = rel.joins("LEFT JOIN music_sessions AS sessions ON sessions.user_id = users.id")
rel = rel.joins("LEFT JOIN recordings AS records ON records.owner_id = users.id")
rel = rel.order("play_count DESC")
when :followed
rel = rel.joins('left outer join follows on follows.followable_id = users.id')
rel = rel.select('count(follows.user_id) as search_follow_count')
rel = rel.order('search_follow_count DESC')
when :playing
rel = rel.joins("inner JOIN connections ON connections.user_id = users.id")
rel = rel.where(['connections.aasm_state != ?', 'expired'])
end
unless locidispid.nil?
rel = rel.order('scores.score')
end
rel = rel.order('users.created_at DESC')
# rel = rel.select(sel_str)
rel, page = self.relation_pagination(rel, params)
rel = rel.includes([:instruments, :followings, :friends])
objs = rel.all
# puts "======================== objs #{objs.inspect}"
srch = Search.new
srch.search_type = :musicians_filter
srch.page_num, srch.page_count = page, objs.total_pages
srch.musician_results_for_user(objs, current_user)
srch.musician_results_for_user(objs, user)
end
def self.relation_pagination(rel, params)
@ -273,13 +324,28 @@ module JamRuby
false
end
def self.new_musicians(usr, since_date=Time.now - 1.week, max_count=50, radius=M_MILES_DEFAULT)
rel = User.musicians
def self.new_musicians(usr, since_date)
# this attempts to find interesting musicians to tell another musician about where interesting
# is "has a good score and was created recently"
# we're sort of depending upon usr being a musicians_geocoded as well...
# this appears to only be called from EmailBatchNewMusician#deliver_batch_sets! which is
# an offline process and thus uses the last jam location as "home base"
locidispid = usr.last_jam_locidispid
score_limit = 60
limit = 50
rel = User.musicians_geocoded
.where(['created_at >= ? AND users.id != ?', since_date, usr.id])
.within(radius, :origin => [usr.lat, usr.lng])
.order('created_at DESC')
.limit(max_count)
.joins('inner join scores on users.last_jam_locidispid = scores.alocidispid')
.where(['scores.blocidispid = ?', locidispid])
.where(['scores.score <= ?', score_limit])
.order('scores.score') # best scores first
.order('users.created_at DESC') # then most recent
.limit(limit)
objs = rel.all.to_a
if block_given?
yield(objs) if 0 < objs.count
else

View File

@ -12,9 +12,9 @@ module JamRuby
include Geokit::ActsAsMappable::Glue unless defined?(acts_as_mappable)
acts_as_mappable
after_save :check_lat_lng
# after_save :check_lat_lng
attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :birth_date, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_large_fpfile, :cropped_s3_path, :cropped_large_s3_path, :photo_url, :large_photo_url, :crop_selection, :lat, :lng
attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :birth_date, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_large_fpfile, :cropped_s3_path, :cropped_large_s3_path, :photo_url, :large_photo_url, :crop_selection
# updating_password corresponds to a lost_password
attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field, :mods_json
@ -140,7 +140,7 @@ module JamRuby
scope :musicians, where(:musician => true)
scope :fans, where(:musician => false)
scope :geocoded_users, where(['lat IS NOT NULL AND lng IS NOT NULL'])
scope :geocoded_users, where(User.arel_table[:last_jam_locidispid].not_eq(nil))
scope :musicians_geocoded, musicians.geocoded_users
scope :email_opt_in, where(:subscribe_email => true)
@ -221,11 +221,11 @@ module JamRuby
end
def location= location_hash
unless location_hash.blank?
unless location_hash.nil?
self.city = location_hash[:city]
self.state = location_hash[:state]
self.country = location_hash[:country]
end if self.city.blank?
end
end
def musician?
@ -776,12 +776,20 @@ module JamRuby
end
user.admin = false
user.city = location[:city]
user.state = location[:state]
user.country = location[:country]
user.location = location
# user.city = location[:city]
# user.state = location[:state]
# user.country = location[:country]
user.birth_date = birth_date
if user.musician # only update instruments if the user is a musician
if musician
user.last_jam_addr = location[:addr]
user.last_jam_locidispid = location[:locidispid]
user.last_jam_updated_reason = 'r'
user.last_jam_updated_at = Time.now
end
if musician # only update instruments if the user is a musician
unless instruments.nil?
instruments.each do |musician_instrument_param|
instrument = Instrument.find(musician_instrument_param[:instrument_id])
@ -1095,55 +1103,64 @@ module JamRuby
!self.city.blank? && (!self.state.blank? || !self.country.blank?)
end
def check_lat_lng
if (city_changed? || state_changed? || country_changed?) && !lat_changed? && !lng_changed?
update_lat_lng
end
end
# def check_lat_lng
# if (city_changed? || state_changed? || country_changed?) && !lat_changed? && !lng_changed?
# update_lat_lng
# end
# end
def update_lat_lng(ip_addy=nil)
if provides_location? # ip_addy argument ignored in this case
return false unless ip_addy.nil? # do nothing if attempting to set latlng from an ip address
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
geo.lat = nil if geo.lat = 0
geo.lng = nil if geo.lng = 0
if geo.lat && geo.lng && (self.lat != geo.lat || self.lng != geo.lng)
self.update_attributes({ :lat => geo.lat, :lng => geo.lng })
return true
end
end
elsif ip_addy
if geo = MaxMindGeo.ip_lookup(ip_addy)
geo.lat = nil if geo.lat = 0
geo.lng = nil if geo.lng = 0
if self.lat != geo.lat || self.lng != geo.lng
self.update_attributes({ :lat => geo.lat, :lng => geo.lng })
return true
end
end
else
if self.lat || self.lng
self.update_attributes({ :lat => nil, :lng => nil })
return true
end
end
false
end
# def update_lat_lng(ip_addy=nil)
# if provides_location? # ip_addy argument ignored in this case
# return false unless ip_addy.nil? # do nothing if attempting to set latlng from an ip address
# 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
# geo.lat = nil if geo.lat = 0
# geo.lng = nil if geo.lng = 0
# if geo.lat && geo.lng && (self.lat != geo.lat || self.lng != geo.lng)
# self.update_attributes({ :lat => geo.lat, :lng => geo.lng })
# return true
# end
# end
# elsif ip_addy
# if geo = MaxMindGeo.ip_lookup(ip_addy)
# geo.lat = nil if geo.lat = 0
# geo.lng = nil if geo.lng = 0
# if self.lat != geo.lat || self.lng != geo.lng
# self.update_attributes({ :lat => geo.lat, :lng => geo.lng })
# return true
# end
# end
# else
# if self.lat || self.lng
# self.update_attributes({ :lat => nil, :lng => nil })
# return true
# end
# end
# false
# end
def current_city(ip_addy=nil)
unless self.city
if self.lat && self.lng
# todo this is really dumb, you can't compare lat lng for equality
return MaxMindGeo.where(['lat = ? AND lng = ?',self.lat,self.lng]).limit(1).first.try(:city)
elsif ip_addy
return MaxMindGeo.ip_lookup(ip_addy).try(:city)
end
else
return self.city
end
# unless self.city
# if self.lat && self.lng
# # todo this is really dumb, you can't compare lat lng for equality
# return MaxMindGeo.where(['lat = ? AND lng = ?',self.lat,self.lng]).limit(1).first.try(:city)
# elsif ip_addy
# return MaxMindGeo.ip_lookup(ip_addy).try(:city)
# end
# else
# return self.city
# end
self.city
end
def update_addr_loc(connection, reason)
self.last_jam_addr = connection.addr
self.last_jam_locidispid = connection.locidispid
self.last_jam_updated_reason = reason
self.last_jam_updated_at = Time.now
self.save
end
def top_followings
@ -1153,9 +1170,15 @@ module JamRuby
.limit(3)
end
def nearest_musicians
# FIXME: replace with Scotts scoring query
Search.new_musicians(self, Time.now - 1.week)
end
def self.deliver_new_musician_notifications(since_date=nil)
since_date ||= Time.now-1.week
self.geocoded_users.find_each do |usr|
# return musicians with locidispid not null
self.musicians_geocoded.find_each do |usr|
Search.new_musicians(usr, since_date) do |new_nearby|
UserMailer.new_musicians(usr, new_nearby).deliver
end

View File

@ -0,0 +1,15 @@
module JamRuby
class UserProgressEmailer
extend Resque::Plugins::LonelyJob
@queue = :scheduled_user_progress_emailer
@@log = Logging.logger[UserProgressEmailer]
def self.perform
@@log.debug("waking up")
EmailBatchProgression.send_progress_batch
@@log.debug("done")
end
end
end

View File

@ -451,6 +451,12 @@ FactoryGirl.define do
test_emails 4.times.collect { Faker::Internet.safe_email }.join(',')
end
factory :email_batch_new_musician, :class => JamRuby::EmailBatchNewMusician do
end
factory :email_batch_progression, :class => JamRuby::EmailBatchProgression do
end
factory :notification, :class => JamRuby::Notification do
factory :notification_text_message do

View File

@ -21,7 +21,7 @@ describe ClaimedRecording do
@music_session = FactoryGirl.create(:active_music_session, :creator => @user, :musician_access => true)
# @music_session.connections << @connection
@music_session.save
@connection.join_the_session(@music_session, true, nil)
@connection.join_the_session(@music_session, true, nil, @user)
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.reload

View File

@ -1,18 +1,464 @@
require 'spec_helper'
describe EmailBatch do
let (:email_batch) { FactoryGirl.create(:email_batch) }
before(:each) do
BatchMailer.deliveries.clear
after(:each) do
Timecop.return
end
it 'has test emails setup' do
expect(email_batch.test_emails.present?).to be true
expect(email_batch.pending?).to be true
describe 'all users' do
users = email_batch.test_users
expect(email_batch.test_count).to eq(users.count)
# before { pending }
let (:email_batch) { FactoryGirl.create(:email_batch) }
before(:each) do
BatchMailer.deliveries.clear
end
it 'has test emails setup' do
expect(email_batch.test_emails.present?).to be true
expect(email_batch.pending?).to be true
users = email_batch.test_users
expect(email_batch.test_count).to eq(users.count)
end
end
describe 'new musician' do
before { pending }
let (:new_musician_batch) { FactoryGirl.create(:email_batch_new_musician) }
before(:each) do
@u1 = FactoryGirl.create(:user, :lat => 37.791649, :lng => -122.394395, :email => 'jonathan@jamkazam.com', :subscribe_email => true, :created_at => Time.now - 3.weeks)
@u2 = FactoryGirl.create(:user, :lat => 37.791649, :lng => -122.394395, :subscribe_email => true)
@u3 = FactoryGirl.create(:user, :lat => 37.791649, :lng => -122.394395, :subscribe_email => false, :created_at => Time.now - 3.weeks)
@u4 = FactoryGirl.create(:user, :lat => 37.791649, :lng => -122.394395, :subscribe_email => true, :created_at => Time.now - 3.weeks)
end
it 'find new musicians with good score' do
new_musician_batch.fetch_recipients do |new_musicians|
expect(new_musicians.count).to eq(2)
num = (new_musicians.keys.map(&:id) - [@u1.id, @u4.id]).count
expect(num).to eq(0)
end
end
it 'cycles through states properly' do
new_musician_batch.deliver_batch
expect(UserMailer.deliveries.length).to eq(2)
new_musician_batch.reload
expect(new_musician_batch.delivered?).to eq(true)
expect(new_musician_batch.sent_count).to eq(2)
end
end
context 'user progress' do
# before { pending }
def handles_new_users(ebatch, user)
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(0) }
dd = user.created_at + ebatch.days_past_for_trigger_index(0).days
Timecop.travel(dd)
vals = [1,0,0]
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(vals[nn]) }
ebatch.make_set(user, 0)
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(0) }
dd = dd + ebatch.days_past_for_trigger_index(1).days
Timecop.travel(dd)
vals = [0,1,0]
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(vals[nn]) }
ebatch.make_set(user, 1)
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(0) }
dd = dd + ebatch.days_past_for_trigger_index(2).days
Timecop.travel(dd)
vals = [0,0,1]
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(vals[nn]) }
ebatch.make_set(user, 2)
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(0) }
end
def handles_existing_users(ebatch, user)
vals = [1,0,0]
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(vals[nn]) }
dd = user.created_at + ebatch.days_past_for_trigger_index(0).days
Timecop.travel(dd)
ebatch.make_set(user, 0)
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(0) }
dd = dd + ebatch.days_past_for_trigger_index(1).days
Timecop.travel(dd)
vals = [0,1,0]
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(vals[nn]) }
ebatch.make_set(user, 1)
dd = dd + ebatch.days_past_for_trigger_index(2).days
Timecop.travel(dd)
expect(ebatch.fetch_recipients(2).count).to eq(1)
ebatch.make_set(user, 2)
expect(ebatch.fetch_recipients(2).count).to eq(0)
dd = dd + 1
Timecop.travel(dd)
expect(ebatch.fetch_recipients(2).count).to eq(0)
end
def skips_some_days(ebatch, user)
dd = user.created_at + ebatch.days_past_for_trigger_index(1).days
Timecop.travel(dd)
vals = [1,0,0]
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(vals[nn]) }
ebatch.make_set(user, 0)
2.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(0) }
dd = dd + ebatch.days_past_for_trigger_index(1).days
Timecop.travel(dd)
vals = [0,1,0]
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(vals[nn]) }
ebatch.make_set(user, 1)
expect(ebatch.fetch_recipients(2).count).to eq(0)
dd = dd + ebatch.days_past_for_trigger_index(2).days
Timecop.travel(dd)
vals = [0,0,1]
3.times { |nn| expect(ebatch.fetch_recipients(nn).count).to eq(vals[nn]) }
ebatch.make_set(user, 2)
expect(ebatch.fetch_recipients(2).count).to eq(0)
end
def loops_bunch_of_users(ebatch, users)
expect(ebatch.fetch_recipients(0,5).count).to eq(0)
dd = users[0].created_at + ebatch.days_past_for_trigger_index(0).days
Timecop.travel(dd)
expect(ebatch.fetch_recipients(0,5).count).to eq(20)
users.each { |uu| ebatch.make_set(uu, 0) }
expect(ebatch.fetch_recipients(0,5).count).to eq(0)
users.map &:destroy
end
def sends_one_email(existing_user, ebatch)
ProgressMailer.deliveries.clear
ebatch.deliver_batch
expect(ProgressMailer.deliveries.length).to eq(1)
end
describe 'client_notdl' do
# before { pending }
let(:batchp) {
FactoryGirl.create(:email_batch_progression, :sub_type => :client_notdl)
}
let(:user_) { FactoryGirl.create(:user) }
let(:user_existing) {
FactoryGirl.create(:user,
:created_at => Time.now - (2 * batchp.days_past_for_trigger_index(2)).days)
}
after(:each) do
batchp.clear_batch_sets!
Timecop.return
end
it 'sends one email' do
sends_one_email(user_existing, batchp)
end
it 'handles new users' do
handles_new_users(batchp, user_)
end
it 'handles existing users' do
handles_existing_users(batchp, user_existing)
end
it 'skips some days' do
skips_some_days(batchp, user_)
end
it 'loops bunch of users' do
users = []
20.times { |nn| users << FactoryGirl.create(:user) }
loops_bunch_of_users(batchp, users)
end
end
describe 'client_dl_notrun' do
# before { pending }
let(:batchp) {
FactoryGirl.create(:email_batch_progression, :sub_type => :client_dl_notrun)
}
let(:user_) { FactoryGirl.create(:user, :first_downloaded_client_at => Time.now) }
let(:date_in_past) { Time.now - (2 * batchp.days_past_for_trigger_index(2)).days }
let(:user_existing) {
FactoryGirl.create(:user,
:created_at => date_in_past,
:first_downloaded_client_at => date_in_past)
}
after(:each) do
batchp.clear_batch_sets!
Timecop.return
end
it 'sends one email' do
sends_one_email(user_existing, batchp)
end
it 'handles new users' do
handles_new_users(batchp, user_)
end
it 'handles existing users' do
handles_existing_users(batchp, user_existing)
end
it 'skips some days' do
skips_some_days(batchp, user_)
end
it 'loops bunch of users' do
users = []
20.times { |nn| users << FactoryGirl.create(:user, :first_downloaded_client_at => Time.now) }
loops_bunch_of_users(batchp, users)
end
end
describe 'client_run_notgear' do
# before { pending }
let(:batchp) {
FactoryGirl.create(:email_batch_progression, :sub_type => :client_run_notgear)
}
let(:user_) { FactoryGirl.create(:user, :first_ran_client_at => Time.now) }
let(:date_in_past) { Time.now - (2 * batchp.days_past_for_trigger_index(2)).days }
let(:user_existing) {
FactoryGirl.create(:user,
:created_at => date_in_past,
:first_ran_client_at => date_in_past)
}
after(:each) do
batchp.clear_batch_sets!
Timecop.return
end
it 'sends one email' do
sends_one_email(user_existing, batchp)
end
it 'handles new users' do
handles_new_users(batchp, user_)
end
it 'handles existing users' do
handles_existing_users(batchp, user_existing)
end
it 'skips some days' do
skips_some_days(batchp, user_)
end
it 'loops bunch of users' do
users = []
20.times { |nn| users << FactoryGirl.create(:user, :first_ran_client_at => Time.now) }
loops_bunch_of_users(batchp, users)
end
end
describe 'gear_notsess' do
# before { pending }
let(:batchp) {
FactoryGirl.create(:email_batch_progression, :sub_type => :gear_notsess)
}
let(:user_) { FactoryGirl.create(:user, :first_certified_gear_at => Time.now) }
let(:date_in_past) { Time.now - (2 * batchp.days_past_for_trigger_index(2)).days }
let(:user_existing) {
FactoryGirl.create(:user,
:created_at => date_in_past,
:first_certified_gear_at => date_in_past)
}
after(:each) do
batchp.clear_batch_sets!
Timecop.return
end
it 'sends one email' do
sends_one_email(user_existing, batchp)
end
it 'handles new users' do
handles_new_users(batchp, user_)
end
it 'handles existing users' do
handles_existing_users(batchp, user_existing)
end
it 'skips some days' do
skips_some_days(batchp, user_)
end
it 'loops bunch of users' do
users = []
20.times { |nn| users << FactoryGirl.create(:user, :first_certified_gear_at => Time.now) }
loops_bunch_of_users(batchp, users)
end
end
describe 'sess_notgood' do
# before { pending }
let(:batchp) {
FactoryGirl.create(:email_batch_progression, :sub_type => :sess_notgood)
}
let(:user_) { FactoryGirl.create(:user, :first_real_music_session_at => Time.now) }
let(:date_in_past) { Time.now - (2 * batchp.days_past_for_trigger_index(2)).days }
let(:user_existing) {
FactoryGirl.create(:user,
:created_at => date_in_past,
:first_real_music_session_at => date_in_past)
}
after(:each) do
batchp.clear_batch_sets!
Timecop.return
end
it 'sends one email' do
sends_one_email(user_existing, batchp)
end
it 'handles new users' do
handles_new_users(batchp, user_)
end
it 'handles existing users' do
handles_existing_users(batchp, user_existing)
end
it 'skips some days' do
skips_some_days(batchp, user_)
end
it 'loops bunch of users' do
users = []
20.times { |nn| users << FactoryGirl.create(:user, :first_real_music_session_at => Time.now) }
loops_bunch_of_users(batchp, users)
end
end
describe 'sess_notrecord' do
# before { pending }
let(:batchp) {
FactoryGirl.create(:email_batch_progression, :sub_type => :sess_notrecord)
}
let(:user_) { FactoryGirl.create(:user, :first_real_music_session_at => Time.now) }
let(:date_in_past) { Time.now - (2 * batchp.days_past_for_trigger_index(2)).days }
let(:user_existing) {
FactoryGirl.create(:user,
:created_at => date_in_past,
:first_real_music_session_at => date_in_past)
}
after(:each) do
batchp.clear_batch_sets!
Timecop.return
end
it 'sends one email' do
sends_one_email(user_existing, batchp)
end
it 'handles new users' do
handles_new_users(batchp, user_)
end
it 'handles existing users' do
handles_existing_users(batchp, user_existing)
end
it 'skips some days' do
skips_some_days(batchp, user_)
end
it 'loops bunch of users' do
users = []
20.times { |nn| users << FactoryGirl.create(:user, :first_real_music_session_at => Time.now) }
loops_bunch_of_users(batchp, users)
end
end
describe 'reg_notinvite' do
# before { pending }
let(:batchp) {
FactoryGirl.create(:email_batch_progression, :sub_type => :reg_notinvite)
}
let(:user_) { FactoryGirl.create(:user) }
let(:date_in_past) { Time.now - (2 * batchp.days_past_for_trigger_index(2)).days }
let(:user_existing) {
FactoryGirl.create(:user,
:created_at => date_in_past)
}
after(:each) do
batchp.clear_batch_sets!
Timecop.return
end
it 'sends one email' do
sends_one_email(user_existing, batchp)
end
it 'handles new users' do
handles_new_users(batchp, user_)
end
it 'handles existing users' do
handles_existing_users(batchp, user_existing)
end
it 'skips some days' do
skips_some_days(batchp, user_)
end
it 'loops bunch of users' do
users = []
20.times { |nn| users << FactoryGirl.create(:user) }
loops_bunch_of_users(batchp, users)
end
end
describe 'reg_notconnect' do
# before { pending }
let(:batchp) {
FactoryGirl.create(:email_batch_progression, :sub_type => :reg_notconnect)
}
let(:user_) { FactoryGirl.create(:user) }
let(:date_in_past) { Time.now - (2 * batchp.days_past_for_trigger_index(2)).days }
let(:user_existing) {
FactoryGirl.create(:user,
:created_at => date_in_past)
}
after(:each) do
batchp.clear_batch_sets!
Timecop.return
end
it 'sends one email' do
sends_one_email(user_existing, batchp)
end
it 'handles new users' do
handles_new_users(batchp, user_)
end
it 'handles existing users' do
handles_existing_users(batchp, user_existing)
end
it 'skips some days' do
skips_some_days(batchp, user_)
end
it 'loops bunch of users' do
users = []
20.times { |nn| users << FactoryGirl.create(:user) }
loops_bunch_of_users(batchp, users)
end
end
describe 'reg_notlike' do
# before { pending }
let(:batchp) {
FactoryGirl.create(:email_batch_progression, :sub_type => :reg_notlike)
}
let(:user_) { FactoryGirl.create(:user) }
let(:date_in_past) { Time.now - (2 * batchp.days_past_for_trigger_index(2)).days }
let(:user_existing) {
FactoryGirl.create(:user,
:created_at => date_in_past)
}
after(:each) do
batchp.clear_batch_sets!
Timecop.return
end
it 'sends one email' do
sends_one_email(user_existing, batchp)
end
it 'handles new users' do
handles_new_users(batchp, user_)
end
it 'handles existing users' do
handles_existing_users(batchp, user_existing)
end
it 'skips some days' do
skips_some_days(batchp, user_)
end
it 'loops bunch of users' do
users = []
20.times { |nn| users << FactoryGirl.create(:user) }
loops_bunch_of_users(batchp, users)
end
end
end
end

View File

@ -7,13 +7,13 @@ describe GetWork do
end
it "get_work_1" do
x = GetWork.get_work(1)
x = GetWork.get_work(1, 0)
#puts x.inspect
x.should be_nil
end
it "get_work_list_1" do
x = GetWork.get_work_list(1)
x = GetWork.get_work_list(1, 0)
#puts x.inspect
x.should eql([])
end

View File

@ -10,7 +10,7 @@ describe Mix do
@music_session = FactoryGirl.create(:active_music_session, :creator => @user, :musician_access => true)
# @music_session.connections << @connection
@music_session.save
@connection.join_the_session(@music_session, true, nil)
@connection.join_the_session(@music_session, true, nil, @user)
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.claim(@user, "name", "description", Genre.first, true)

View File

@ -46,7 +46,6 @@ describe MusicSession do
music_session.valid?.should be_false
end
end
end

View File

@ -3,32 +3,88 @@ require 'spec_helper'
describe 'Musician search' do
before(:each) do
@geocode1 = FactoryGirl.create(:geocoder)
@geocode2 = FactoryGirl.create(:geocoder)
@users = []
@users << @user1 = FactoryGirl.create(:user)
@users << @user2 = FactoryGirl.create(:user)
@users << @user3 = FactoryGirl.create(:user)
@users << @user4 = FactoryGirl.create(:user)
# @geocode1 = FactoryGirl.create(:geocoder)
# @geocode2 = FactoryGirl.create(:geocoder)
t = Time.now - 10.minute
@user1 = FactoryGirl.create(:user, created_at: t+1.minute, last_jam_locidispid: 1)
@user2 = FactoryGirl.create(:user, created_at: t+2.minute, last_jam_locidispid: 2)
@user3 = FactoryGirl.create(:user, created_at: t+3.minute, last_jam_locidispid: 3)
@user4 = FactoryGirl.create(:user, created_at: t+4.minute, last_jam_locidispid: 4)
@user5 = FactoryGirl.create(:user, created_at: t+5.minute, last_jam_locidispid: 5)
@user6 = FactoryGirl.create(:user, created_at: t+6.minute) # not geocoded
@user7 = FactoryGirl.create(:user, created_at: t+7.minute, musician: false) # not musician
@musicians = []
@musicians << @user1
@musicians << @user2
@musicians << @user3
@musicians << @user4
@musicians << @user5
@musicians << @user6
@geomusicians = []
@geomusicians << @user1
@geomusicians << @user2
@geomusicians << @user3
@geomusicians << @user4
@geomusicians << @user5
Score.delete_all
Score.createx(1, 'a', 1, 1, 'a', 1, 10)
Score.createx(1, 'a', 1, 2, 'b', 2, 20)
Score.createx(1, 'a', 1, 3, 'c', 3, 30)
Score.createx(1, 'a', 1, 4, 'd', 4, 40)
Score.createx(2, 'b', 2, 3, 'c', 3, 15)
Score.createx(2, 'b', 2, 4, 'd', 4, 70)
end
context 'default filter settings' do
it "finds all musicians" do
# expects all the users
num = User.musicians.count
results = Search.musician_filter({ :per_page => num })
expect(results.results.count).to eq(num)
expect(results.search_type).to be(:musicians_filter)
# expects all the musicians (geocoded)
results = Search.musician_filter
results.search_type.should == :musicians_filter
results.results.count.should == @geomusicians.length
results.results.should eq @geomusicians.reverse
end
it "finds musicians with proper ordering" do
# the ordering should be create_at since no followers exist
expect(Follow.count).to eq(0)
results = Search.musician_filter({ :per_page => User.musicians.count })
results.results.each_with_index do |uu, idx|
expect(uu.id).to eq(@users.reverse[idx].id)
end
it "finds all musicians page 1" do
# expects all the musicians
results = Search.musician_filter({page: 1})
results.search_type.should == :musicians_filter
results.results.count.should == @geomusicians.length
results.results.should eq @geomusicians.reverse
end
it "finds all musicians page 2" do
# expects no musicians (all fit on page 1)
results = Search.musician_filter({page: 2})
results.search_type.should == :musicians_filter
results.results.count.should == 0
end
it "finds all musicians page 1 per_page 3" do
# expects three of the musicians
results = Search.musician_filter({per_page: 3})
results.search_type.should == :musicians_filter
results.results.count.should == 3
results.results.should eq @geomusicians.reverse.slice(0, 3)
end
it "finds all musicians page 2 per_page 3" do
# expects two of the musicians
results = Search.musician_filter({page: 2, per_page: 3})
results.search_type.should == :musicians_filter
results.results.count.should == 2
results.results.should eq @geomusicians.reverse.slice(3, 3)
end
it "finds all musicians page 3 per_page 3" do
# expects two of the musicians
results = Search.musician_filter({page: 3, per_page: 3})
results.search_type.should == :musicians_filter
results.results.count.should == 0
end
it "sorts musicians by followers" do
@ -90,7 +146,7 @@ describe 'Musician search' do
f3.save
# @user2.followers.concat([@user3, @user4, @user2])
results = Search.musician_filter({ :per_page => @users.size }, @user3)
results = Search.musician_filter({ :per_page => @musicians.size }, @user3)
expect(results.results[0].id).to eq(@user2.id)
# check the follower count for given entry
@ -109,13 +165,13 @@ describe 'Musician search' do
end
def make_recording(usr)
connection = FactoryGirl.create(:connection, :user => usr)
connection = FactoryGirl.create(:connection, :user => usr, locidispid: usr.last_jam_locidispid)
instrument = FactoryGirl.create(:instrument, :description => 'a great instrument')
track = FactoryGirl.create(:track, :connection => connection, :instrument => instrument)
music_session = FactoryGirl.create(:active_music_session, :creator => usr, :musician_access => true)
# music_session.connections << connection
music_session.save
connection.join_the_session(music_session, true, nil)
# music_session.save
connection.join_the_session(music_session, true, nil, usr)
recording = Recording.start(music_session, usr)
recording.stop
recording.reload
@ -126,11 +182,11 @@ describe 'Musician search' do
end
def make_session(usr)
connection = FactoryGirl.create(:connection, :user => usr)
connection = FactoryGirl.create(:connection, :user => usr, locidispid: usr.last_jam_locidispid)
music_session = FactoryGirl.create(:active_music_session, :creator => usr, :musician_access => true)
# music_session.connections << connection
music_session.save
connection.join_the_session(music_session, true, nil)
# music_session.save
connection.join_the_session(music_session, true, nil, usr)
end
context 'musician stat counters' do
@ -154,8 +210,8 @@ describe 'Musician search' do
# @user4.followers.concat([@user4])
# @user3.followers.concat([@user4])
# @user2.followers.concat([@user4])
expect(@user4.top_followings.count).to be 3
expect(@user4.top_followings.map(&:id)).to match_array((@users - [@user1]).map(&:id))
expect(@user4.top_followings.count).to eq 3
expect(@user4.top_followings.map(&:id)).to match_array((@musicians - [@user1, @user5, @user6]).map(&:id))
end
it "friends stat shows friend count" do
@ -172,6 +228,8 @@ describe 'Musician search' do
end
it "recording stat shows recording count" do
Recording.delete_all
recording = make_recording(@user1)
expect(recording.users.length).to be 1
expect(recording.users.first).to eq(@user1)
@ -182,6 +240,7 @@ describe 'Musician search' do
expect(@user1.recordings.detect { |rr| rr == recording }).to_not be_nil
results = Search.musician_filter({},@user1)
# puts "====================== results #{results.inspect}"
uu = results.results.detect { |mm| mm.id == @user1.id }
expect(uu).to_not be_nil
@ -194,31 +253,39 @@ describe 'Musician search' do
context 'musician sorting' do
it "by plays" do
Recording.delete_all
make_recording(@user1)
# order results by num recordings
results = Search.musician_filter({ :orderby => 'plays' }, @user2)
# puts "========= results #{results.inspect}"
expect(results.results.length).to eq(2)
expect(results.results[0].id).to eq(@user1.id)
expect(results.results[1].id).to eq(@user3.id)
# add more data and make sure order still correct
make_recording(@user2); make_recording(@user2)
make_recording(@user3)
make_recording(@user3)
results = Search.musician_filter({ :orderby => 'plays' }, @user2)
expect(results.results[0].id).to eq(@user2.id)
expect(results.results.length).to eq(2)
expect(results.results[0].id).to eq(@user3.id)
expect(results.results[1].id).to eq(@user1.id)
end
it "by now playing" do
# should get 1 result with 1 active session
make_session(@user3)
make_session(@user1)
results = Search.musician_filter({ :orderby => 'playing' }, @user2)
expect(results.results.count).to be 1
expect(results.results.first.id).to eq(@user3.id)
expect(results.results.first.id).to eq(@user1.id)
# should get 2 results with 2 active sessions
# sort order should be created_at DESC
make_session(@user4)
make_session(@user3)
results = Search.musician_filter({ :orderby => 'playing' }, @user2)
expect(results.results.count).to be 2
expect(results.results[0].id).to eq(@user4.id)
expect(results.results[1].id).to eq(@user3.id)
expect(results.results[0].id).to eq(@user3.id)
expect(results.results[1].id).to eq(@user1.id)
end
end
@ -275,28 +342,43 @@ describe 'Musician search' do
context 'new users' do
it "find nearby" do
# create new user outside 500 from Apex to ensure its excluded from results
FactoryGirl.create(:user, {city: "Austin", state: "TX", country: "US"})
User.geocoded_users.find_each do |usr|
Search.new_musicians(usr) do |new_usrs|
# the newly created user is not nearby the existing users (which are in Apex, NC)
# and that user is not included in query
expect(new_usrs.count).to eq(User.musicians.count - 1)
end
end
it "find three for user1" do
# user2..4 are scored against user1
ms = Search.new_musicians(@user1, Time.now - 1.week)
ms.should_not be_nil
ms.length.should == 3
ms.should eq [@user2, @user3, @user4]
end
it "sends new musician email" do
# create new user outside 500 from Apex to ensure its excluded from results
FactoryGirl.create(:user, {city: "Austin", state: "TX", country: "US"})
User.geocoded_users.find_each do |usr|
Search.new_musicians(usr) do |new_usrs|
# the newly created user is not nearby the existing users (which are in Apex, NC)
# and that user is not included in query
expect(new_usrs.count).to eq(User.musicians.count - 1)
end
end
it "find two for user2" do
# user1,3,4 are scored against user1, but user4 is bad
ms = Search.new_musicians(@user2, Time.now - 1.week)
ms.should_not be_nil
ms.length.should == 2
ms.should eq [@user3, @user1]
end
it "find two for user3" do
# user1..2 are scored against user3
ms = Search.new_musicians(@user3, Time.now - 1.week)
ms.should_not be_nil
ms.length.should == 2
ms.should eq [@user2, @user1]
end
it "find one for user4" do
# user1..2 are scored against user4, but user2 is bad
ms = Search.new_musicians(@user4, Time.now - 1.week)
ms.should_not be_nil
ms.length.should == 1
ms.should eq [@user1]
end
it "find none for user5" do
# user1..4 are not scored against user5
ms = Search.new_musicians(@user5, Time.now - 1.week)
ms.should_not be_nil
ms.length.should == 0
end
end

View File

@ -80,7 +80,7 @@ describe Recording do
@track2 = FactoryGirl.create(:track, :connection => @connection2, :instrument => @instrument2)
# @music_session.connections << @connection2
@connection2.join_the_session(@music_session, true, nil)
@connection2.join_the_session(@music_session, true, nil, @user2)
@recording = Recording.start(@music_session, @user)
@user.recordings.length.should == 0
@ -179,7 +179,7 @@ describe Recording do
@track = FactoryGirl.create(:track, :connection => @connection2, :instrument => @instrument)
# @music_session.connections << @connection2
@music_session.save
@connection2.join_the_session(@music_session, true, nil)
@connection2.join_the_session(@music_session, true, nil, @user2)
@recording = Recording.start(@music_session, @user)
@recording.stop
@recording.reload

View File

@ -4,7 +4,7 @@ describe Score do
before do
Score.delete_all
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil)
Score.createx(1234, 'anodeid', 0x01020304, 2345, 'bnodeid', 0x02030405, 20, nil, 'foo')
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 30, nil)
Score.createx(1234, 'anodeid', 0x01020304, 3456, 'cnodeid', 0x03040506, 40, Time.new.utc-3600)
end
@ -25,6 +25,7 @@ describe Score do
s.score.should == 20
s.scorer.should == 0
s.score_dt.should_not be_nil
s.scoring_data.should eq('foo')
end
it 'b to a' do
@ -39,6 +40,7 @@ describe Score do
s.score.should == 20
s.scorer.should == 1
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'a to c' do
@ -53,6 +55,7 @@ describe Score do
s.score.should == 30
s.scorer.should == 0
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'c to a' do
@ -67,6 +70,7 @@ describe Score do
s.score.should == 30
s.scorer.should == 1
s.score_dt.should_not be_nil
s.scoring_data.should be_nil
end
it 'delete a to c' do
@ -92,4 +96,31 @@ describe Score do
Score.findx(3456, 3456).should == -1
end
it "test shortcut for making scores from connections" do
user1 = FactoryGirl.create(:user)
conn1 = FactoryGirl.create(:connection, user: user1, addr: 0x01020304, locidispid: 5)
user2 = FactoryGirl.create(:user)
conn2 = FactoryGirl.create(:connection, user: user2, addr: 0x11121314, locidispid: 6)
user3 = FactoryGirl.create(:user)
conn3 = FactoryGirl.create(:connection, user: user3, addr: 0x21222324, locidispid: 7)
Score.findx(5, 6).should == -1
Score.findx(6, 5).should == -1
Score.findx(5, 7).should == -1
Score.findx(7, 5).should == -1
Score.findx(6, 7).should == -1
Score.findx(7, 6).should == -1
Score.score_conns(conn1, conn2, 12)
Score.score_conns(conn1, conn3, 13)
Score.score_conns(conn2, conn3, 23)
Score.findx(5, 6).should == 12
Score.findx(6, 5).should == 12
Score.findx(5, 7).should == 13
Score.findx(7, 5).should == 13
Score.findx(6, 7).should == 23
Score.findx(7, 6).should == 23
end
end

View File

@ -10,13 +10,13 @@ X If no profile location is provided, and the user creates/joins a music session
=end
before do
@geocode1 = FactoryGirl.create(:geocoder)
@geocode2 = FactoryGirl.create(:geocoder)
@user = User.new(first_name: "Example", last_name: "User", email: "user@example.com",
password: "foobar", password_confirmation: "foobar",
city: "Apex", state: "NC", country: "US",
terms_of_service: true, musician: true)
@user.save!
# @geocode1 = FactoryGirl.create(:geocoder)
# @geocode2 = FactoryGirl.create(:geocoder)
# @user = User.new(first_name: "Example", last_name: "User", email: "user@example.com",
# password: "foobar", password_confirmation: "foobar",
# city: "Apex", state: "NC", country: "US",
# terms_of_service: true, musician: true)
# @user.save!
end
describe "with profile location data" do

View File

@ -30,7 +30,7 @@ require 'timecop'
require 'resque_spec/scheduler'
# uncomment this to see active record logs
#ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base)
# ActiveRecord::Base.logger = Logger.new(STDOUT) if defined?(ActiveRecord::Base)
include JamRuby

View File

@ -5,7 +5,7 @@ unless ENV["LOCAL_DEV"] == "1"
end
# Look for $WORKSPACE, otherwise use "workspace" as dev path.
devenv = ENV["BUILD_NUMBER"].nil? || ENV["TEST_WWW"] == "1"
devenv = ENV["BUILD_NUMBER"].nil? # Jenkins sets a build number environment variable
if devenv
gem 'jam_db', :path=> "../db/target/ruby_package"

View File

@ -139,14 +139,14 @@
$form.submit(sendMessage);
$textBox.keydown(handleEnter);
$sendChatMessageBtn.click(sendMessage);
registerChatMessage(bind);
}
else {
$form.submit(null);
$textBox.keydown(null);
$sendChatMessageBtn.click(null);
}
registerChatMessage(bind);
}
// called from sidebar when messages come in

View File

@ -34,8 +34,8 @@
function StartRecording(recordingId, clients) {
startingSessionState = {};
// we expect all clients to respond within 3 seconds to mimic the reliable UDP layer
startingSessionState.aggegratingStartResultsTimer = setTimeout(timeoutStartRecordingTimer, 3000);
// we expect all clients to respond within 1 seconds to mimic the reliable UDP layer
startingSessionState.aggegratingStartResultsTimer = setTimeout(timeoutStartRecordingTimer, 1000);
startingSessionState.recordingId = recordingId;
startingSessionState.groupedClientTracks = copyClientIds(clients, app.clientId); // we will manipulate this new one
@ -70,8 +70,8 @@
stoppingSessionState = {};
// we expect all clients to respond within 3 seconds to mimic the reliable UDP layer
stoppingSessionState.aggegratingStopResultsTimer = setTimeout(timeoutStopRecordingTimer, 3000);
// we expect all clients to respond within 1 seconds to mimic the reliable UDP layer
stoppingSessionState.aggegratingStopResultsTimer = setTimeout(timeoutStopRecordingTimer, 1000);
stoppingSessionState.recordingId = recordingId;
stoppingSessionState.groupedClientTracks = copyClientIds(clients, app.clientId);

View File

@ -79,7 +79,10 @@
band : 'Band',
fan : 'Fan',
recording : 'Recording',
session : 'Session'
session : 'Session',
facebook: 'facebook',
twitter: 'twitter',
google: 'google',
};
var categories = {
@ -271,11 +274,11 @@
context.ga('send', 'event', categories.band, bandAction);
}
function trackJKSocial(category, target) {
function trackJKSocial(category, target, data) {
assertOneOf(category, categories);
assertOneOf(target, jkSocialTargets);
context.ga('send', 'event', category, target);
context.ga('send', 'event', category, target, data);
}

View File

@ -385,6 +385,7 @@
})
.on('stoppedRecording', function(e, data) {
if(data.reason) {
logger.warn("Recording Discarded: ", data);
var reason = data.reason;
var detail = data.detail;

View File

@ -657,7 +657,10 @@
}
context.JK.clientType = function () {
return context.jamClient.IsNativeClient() ? 'client' : 'browser';
if (context.jamClient) {
return context.jamClient.IsNativeClient() ? 'client' : 'browser';
}
return 'browser';
}
/**
* Returns 'MacOSX' if the os appears to be macintosh,

View File

@ -13,6 +13,12 @@ body.web {
}
}
}
.share_links {
position: absolute;
top: 116px;
left: 1100px;
z-index: 10;
}
.buzz {
width: 300px;

View File

@ -7,13 +7,12 @@ class ApiScoringController < ApiController
clientid = params[:clientid]
if clientid.nil? then render :json => {message: 'clientid not specified'}, :status => 400; return end
conn = Connection.where(client_id: clientid).first
conn = Connection.where(client_id: clientid, user_id: current_user.id).first
if conn.nil? then render :json => {message: 'session not found'}, :status => 404; return end
if !current_user.id.eql?(conn.user.id) then render :json => {message: 'session not owned by user'}, :status => 403; return end
# if !current_user.id.eql?(conn.user.id) then render :json => {message: 'session not owned by user'}, :status => 403; return end
# todo this method is a stub
#puts "ApiScoringController#work(#{clientid}) => locidispid #{c.locidispid}"
result_client_id = JamRuby::GetWork.get_work(conn.locidispid)
result_client_id = JamRuby::GetWork.get_work(conn.locidispid, conn.addr)
#result_client_id = clientid+'peer'
render :json => {:clientid => result_client_id}, :status => 200
@ -23,12 +22,11 @@ class ApiScoringController < ApiController
clientid = params[:clientid]
if clientid.nil? then render :json => {message: 'clientid not specified'}, :status => 400; return end
conn = Connection.where(client_id: clientid).first
conn = Connection.where(client_id: clientid, user_id: current_user.id).first
if conn.nil? then render :json => {message: 'session not found'}, :status => 404; return end
if !current_user.id.eql?(conn.user.id) then render :json => {message: 'session not owned by user'}, :status => 403; return end
# if !current_user.id.eql?(conn.user.id) then render :json => {message: 'session not owned by user'}, :status => 403; return end
# todo this method is a stub
result_client_ids = JamRuby::GetWork.get_work_list(conn.locidispid)
result_client_ids = JamRuby::GetWork.get_work_list(conn.locidispid, conn.addr)
#result_client_ids = [clientid+'peer1', clientid+'peer2']
render :json => {:clientids => result_client_ids}, :status => 200
@ -37,11 +35,13 @@ class ApiScoringController < ApiController
def record # aclientid, aAddr, bclientid, bAddr, score returns nothing
#puts "================= record #{params.inspect}"
aclientid = params[:aclientid]
aip_address = params[:aAddr]
bclientid = params[:bclientid]
bip_address = params[:bAddr]
score = params[:score]
score_data = params.to_s
if aclientid.nil? then render :json => {message: 'aclientid not specified'}, :status => 400; return end
if aip_address.nil? then render :json => {message: 'aAddr not specified'}, :status => 400; return end
@ -61,10 +61,10 @@ class ApiScoringController < ApiController
if !score.is_a? Numeric then render :json => {message: 'score not valid numeric'}, :status => 400; return end
aconn = Connection.where(client_id: aclientid).first
aconn = Connection.where(client_id: aclientid, user_id: current_user.id).first
if aconn.nil? then render :json => {message: 'a\'s session not found'}, :status => 404; return end
if aAddr != aconn.addr then render :json => {message: 'a\'s session addr does not match aAddr'}, :status => 403; return end
if !current_user.id.eql?(aconn.user.id) then render :json => {message: 'a\'s session not owned by user'}, :status => 403; return end
# if !current_user.id.eql?(aconn.user.id) then render :json => {message: 'a\'s session not found'}, :status => 403; return end
bconn = Connection.where(client_id: bclientid).first
if bconn.nil? then render :json => {message: 'b\'s session not found'}, :status => 404; return end
@ -82,7 +82,7 @@ class ApiScoringController < ApiController
if bisp.nil? or bloc.nil? then render :json => {message: 'b\'s location or isp not found'}, :status => 404; return end
blocidispid = bloc.locid*1000000+bisp.coid
JamRuby::Score.createx(alocidispid, aclientid, aAddr, blocidispid, bclientid, bAddr, score.ceil, nil)
JamRuby::Score.createx(alocidispid, aclientid, aAddr, blocidispid, bclientid, bAddr, score.ceil, nil, score_data)
render :json => {}, :status => 200
end

View File

@ -7,15 +7,17 @@ class ApiSearchController < ApiController
def index
if 1 == params[Search::PARAM_MUSICIAN].to_i || 1 == params[Search::PARAM_BAND].to_i
# puts "================== params #{params.to_s}"
query = params.clone
query[:remote_ip] = request.remote_ip
if 1 == params[Search::PARAM_MUSICIAN].to_i
@search = Search.musician_filter(query, current_user)
if 1 == query[Search::PARAM_MUSICIAN].to_i
clientid = query[:clientid]
conn = (clientid ? Connection.where(client_id: clientid, user_id: current_user.id).first : nil)
@search = Search.musician_filter(query, current_user, conn)
else
@search = Search.band_filter(query, current_user)
end
respond_with @search, responder: ApiResponder, :status => 200
elsif 1 == params[Search::PARAM_SESSION_INVITE].to_i
@search = Search.session_invite_search(params[:query], current_user)
else

View File

@ -370,20 +370,35 @@ class UsersController < ApplicationController
end
def endorse
if uu = User.where(['id = ? AND first_liked_us IS NULL',params[:id]]).limit(1).first
uu.first_liked_us = Time.now
if uu = current_user ||
uu = User.where(['id = ? AND first_social_promoted_at IS NULL',params[:id]]).limit(1).first
uu.first_social_promoted_at = Time.now
uu.save!
end if params[:id].present?
end if params[:id].present? && (service=params[:service]).present?
url, service = 'http://www.jamkazam.com', params[:service]
service ||= 'facebook'
url = CGI::escape('http://www.jamkazam.com')
txt = CGI::escape('Check out JamKazam -- Play music together over the Internet as if in the same room')
if 'twitter'==service
url = 'https://twitter.com/jamkazam'
url = "https://twitter.com/intent/tweet?text=#{txt}&url=#{url}"
elsif 'facebook'==service
url = 'https://www.facebook.com/JamKazam'
url = "http://www.facebook.com/sharer/sharer.php?u=#{url}&t=#{txt}"
elsif 'google'==service
url = 'https://plus.google.com/u/0/106619885929396862606/about'
url = "https://plus.google.com/share?url=#{url}"
end
if 'email'==params[:src]
js =<<JS
<script type="text/javascript">
$(function() {
JK.GA.trackJKSocial(JK.GA.Categories.jkLike, '#{service}', 'email');
window.location = "#{url}";
});
</script>
JS
render :inline => js, :layout => 'landing'
else
redirect_to url
end
redirect_to url
end
private

View File

@ -1,6 +1,5 @@
<!-- start footer -->
<div id="footer">
<!-- copyright -->
<div id="copyright">Copyright &copy; <%= Time.now.year %> JamKazam, Inc. All Rights Reserved</div>

View File

@ -1,4 +1,4 @@
<div class="dialog-overlay-sm" layout="dialog" layout-id="rate-session-dialog" id="rate-session-dialog">
<div class="dialog" layout="dialog" layout-id="rate-session-dialog" id="rate-session-dialog">
<!-- dialog header -->
<div class="content-head">
<%= image_tag "shared/icon_session.png", {:height => 19, :width => 19, :class => "content-icon"} %>

View File

@ -102,7 +102,7 @@
</div>
<div layout-panel="contents" class="chatcontents">
<div class="chat-status">
<span>Chat is available during session is connected.</span>
<span>Chat is available when session is connected.</span>
</div>
<div class="chat-list-scroller">
<div class="previous-chat-list">

View File

@ -5,6 +5,10 @@
= link_to image_tag("web/cta_button.png", :alt => "Sign up now for your free account!"), signup_path, class: "signup", id: "signup"
.clearleft
= link_to "Already have an account?", signin_path, class: "signin", id: "signin"
.share_links
- [:twitter, :facebook, :google].each do |site|
= link_to(image_tag("content/icon_#{site}.png", :style => "vertical-align:top"), "javascript:void(0)", {'data-site' => site, :class => 'like-link'})
= ' '
- content_for :after_black_bar do
- if @jamfest_2014
@ -24,6 +28,12 @@
:javascript
$(function () {
window.JK.WelcomePage();
$('.like-link').click(function() {
var like_site = $(this).data('site');
JK.GA.trackJKSocial(JK.GA.Categories.jkLike, like_site, JK.clientType());
window.open("/endorse/0/"+like_site, '_blank');
});
})
- content_for :extra_dialogs do

Some files were not shown because too many files have changed in this diff Show More