merge conflict with db/manifest resolved

This commit is contained in:
Scott Comer 2014-05-21 21:33:38 -05:00
commit 94421c03c0
49 changed files with 1976 additions and 39 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"

View File

@ -156,4 +156,5 @@ scheduled_sessions.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

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;

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

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

@ -138,10 +138,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

@ -106,9 +106,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

@ -78,13 +78,22 @@ FOO
self.test_emails.present? && (self.tested? || self.pending?)
end
def deliver_batch
self.perform_event('do_batch_run!')
def deliver_batch_sets!
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))
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!')
self.deliver_batch_sets!
end
def test_count
self.test_emails.split(',').count
end
@ -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

@ -1170,6 +1170,11 @@ 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
# todo scott return musicians with locidispid not null

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

@ -445,6 +445,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

@ -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
# before { pending }
users = email_batch.test_users
expect(email_batch.test_count).to eq(users.count)
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

@ -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

@ -371,8 +371,8 @@ class UsersController < ApplicationController
def endorse
if uu = current_user ||
uu = User.where(['id = ? AND first_liked_us IS NULL',params[:id]]).limit(1).first
uu.first_liked_us = Time.now
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? && (service=params[:service]).present?

View File

@ -24,3 +24,7 @@ EmailErrorCollector:
class: "JamRuby::EmailErrorCollector"
description: "Collects sendgrid email errors"
UserProgressEmailer:
cron: "30 21 * * *"
class: "JamRuby::UserProgressEmailer"
description: "Sends periodic user progress emails"

View File

@ -30,7 +30,7 @@ db_config = YAML::load(File.open('config/database.yml'))["test"]
bputs "before recreate db"
SpecDb::recreate_database(db_config) unless ENV["TEST_WWW"] == "1"
SpecDb::recreate_database(db_config)
bputs "before connect db"
ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))["test"])
@ -162,9 +162,6 @@ bputs "before register capybara"
config.filter_run_excluding slow: true unless ENV['RUN_SLOW_TESTS'] == "1" || ENV['SLOW'] == "1" || ENV['ALL_TESTS'] == "1"
config.filter_run_excluding aws: true unless ENV['RUN_AWS_TESTS'] == "1" || ENV['AWS'] == "1" || ENV['ALL_TESTS'] == "1"
# by default, do not run production web tests -- even when "ALL_TESTS" is desired
config.filter_run_excluding test_www: true unless ENV['TEST_WWW'] == "1"
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"

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?
if devenv
gem 'jam_db', :path=> "../db/target/ruby_package"