merging develop

This commit is contained in:
Jonathan Kolyer 2015-08-04 13:44:47 +00:00
commit aa36b2f215
371 changed files with 13873 additions and 2103 deletions

View File

@ -14,7 +14,7 @@ else
ENV['NOKOGIRI_USE_SYSTEM_LIBRARIES'] ||= "true"
end
gem 'rails', '~> 3.2.11'
gem 'rails', '~> 3.2.22'
gem 'bootstrap-sass', '2.0.4'
gem 'bcrypt-ruby', '3.0.1'
@ -44,7 +44,7 @@ gem 'rails3-jquery-autocomplete'
gem 'activeadmin' #, github: 'activeadmin', branch: '0-6-stable'
gem 'mime-types', '1.25'
gem 'meta_search'
gem 'fog', "~> 1.18.0"
gem 'fog', "~> 1.32.0"
gem 'unf', '0.1.3' #optional fog dependency
gem 'country-select'
gem 'aasm', '3.0.16'
@ -58,7 +58,7 @@ gem 'resque'
gem 'resque-retry'
gem 'resque-failed-job-mailer'
gem 'resque-lonely_job', '~> 1.0.0'
gem 'eventmachine', '1.0.3'
gem 'eventmachine', '1.0.4'
gem 'amqp', '0.9.8'
gem 'logging-rails', :require => 'logging/rails'
gem 'pg_migrate'
@ -77,7 +77,7 @@ gem 'influxdb-rails', '0.1.10'
gem 'recurly'
group :libv8 do
gem 'libv8', "~> 3.11.8"
gem 'libv8', "~> 4.5.95"
end
@ -101,15 +101,16 @@ end
group :development, :test do
gem 'capybara'
gem 'rspec-rails', '2.14.2'
gem 'guard-rspec', '0.5.5'
gem 'guard-rspec'
gem 'jasmine', '1.3.1'
gem 'execjs', '1.4.0'
gem 'therubyracer' #, '0.11.0beta8'
#gem 'therubyracer' #, '0.11.0beta8'
gem 'factory_girl_rails', '4.1.0'
gem 'database_cleaner', '0.7.0'
gem 'launchy'
gem 'faker', '1.3.0'
gem 'puma'
gem 'test-unit'
end
group :test do

View File

@ -5,9 +5,14 @@ ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do
config.sort_order = 'name_asc'
config.batch_actions = false
filter :genre
filter :genres
filter :status, :as => :select, collection: JamRuby::JamTrack::STATUS
scope("Default", default: true) { |scope| scope }
scope("Onboarding TODO") { |scope| scope.where('onboarding_exceptions is not null') }
scope("Tency Only") { |scope| scope.joins('INNER JOIN jam_track_licensors as licensors ON jam_tracks.licensor_id = licensors.id').where("licensors.name = 'Tency Music'") }
scope("Onboarding TODO w/ Tency Only") { |scope| scope.joins('INNER JOIN jam_track_licensors as licensors ON jam_tracks.licensor_id = licensors.id').where("licensors.name = 'Tency Music'").where('onboarding_exceptions is not null') }
form :partial => 'form'
index do
@ -24,11 +29,21 @@ ActiveAdmin.register JamRuby::JamTrack, :as => 'JamTracks' do
column :original_artist
column :name
column :onboarding_flags do |jam_track| jam_track.onboard_warnings end
column :onboarding_exceptions do |jam_track|
if jam_track.onboarding_exceptions
exceptions = JSON.parse(jam_track.onboarding_exceptions)
exceptions.keys.join(',')
else
''
end
end
column :status
column :master_track do |jam_track| jam_track.master_track.nil? ? 'None' : (link_to "Download", jam_track.master_track.url_by_sample_rate(44)) end
column :licensor
column :genre
column :genres do |jam_track|
jam_track.genres.map(&:description).join(',')
end
column :price
column :reproduction_royalty
column :public_performance_royalty

View File

@ -12,7 +12,7 @@
= f.input :songwriter, :input_html => { :rows=>1, :maxlength=>1000 }
= f.input :publisher, :input_html => { :rows=>1, :maxlength=>1000 }
= f.input :licensor, collection: JamRuby::JamTrackLicensor.all, include_blank: true
= f.input :genre, collection: JamRuby::Genre.all, include_blank: false
= f.input :genres
= f.input :duration, hint: 'this should rarely need editing because it comes from the import process'
= f.input :sales_region, collection: JamRuby::JamTrack::SALES_REGION, include_blank: false
= f.input :price, :required => true, :input_html => {type: 'numeric'}

View File

@ -1,2 +1,2 @@
<%- headers = ['email', 'name', 'unsubscribe_token'] -%>
<%= CSV.generate_line headers %><%- @users.each do |user| -%><%= CSV.generate_line([user.email, user.name, user.unsubscribe_token]) %><%- end -%>
<%= CSV.generate_line headers %><%- @users.each do |user| -%><%= CSV.generate_line([user.email, user.first_name, user.unsubscribe_token]) %><%- end -%>

View File

@ -1,5 +1,10 @@
ActionMailer::Base.raise_delivery_errors = true
begin
ActionMailer::Base.delivery_method = Rails.env == "test" ? :test : :smtp
rescue
# this can happen on the build server when it's compiling assets and doesn't have the 'jam' database
ActionMailer::Base.delivery_method = :test
end
ActionMailer::Base.smtp_settings = {
:address => Rails.application.config.email_smtp_address,
:port => Rails.application.config.email_smtp_port,

View File

@ -16,7 +16,6 @@ class JamRuby::JamTrack
end
def jmep_json_generate
self.genre_id = nil if self.genre_id == ''
self.licensor_id = nil if self.licensor_id == ''
self.jmep_json = nil if self.jmep_json == ''
self.time_signature = nil if self.time_signature == ''

View File

@ -4,4 +4,4 @@
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
JamAdmin::Application.config.secret_token = 'e27a8deff5bc124d1c74cb86ebf38ac4a246091b859bcccf4f8076454e0ff3e04ffc87c9a0f4ddc801c4753e20b2a3cf06e5efc815cfe8e6377f912b737c5f77'
JamAdmin::Application.config.secret_token = 'ced345e01611593c1b783bae98e4e56dbaee787747e92a141565f7c61d0ab2c6f98f7396fb4b178258301e2713816e158461af58c14b695901692f91e72b6200'

3
build
View File

@ -62,7 +62,8 @@ popd > /dev/null
if [ ! -z "$PACKAGE" ]; then
DEB_SERVER=http://localhost:9010/apt-`uname -p`
source /etc/lsb-release
DEB_SERVER=https://int.jamkazam.com:9010/apt-`uname -p`/$DISTRIB_CODENAME
GEM_SERVER=http://localhost:9000/gems
# if still going, then push all debs up

View File

@ -3,4 +3,4 @@ source 'http://rubygems.org'
# Assumes you have already cloned pg_migrate_ruby in your workspace
# $ cd [workspace]
# $ git clone https://github.com/sethcall/pg_migrate_ruby
gem 'pg_migrate', '0.1.13'
gem 'pg_migrate', '0.1.13', :source => 'http://rubygems.org/'

View File

@ -15,7 +15,7 @@ PLATFORMS
ruby
DEPENDENCIES
pg_migrate (= 0.1.13)
pg_migrate (= 0.1.13)!
BUNDLED WITH
1.10.3
1.10.5

View File

@ -31,6 +31,7 @@ if [ ! -z "$PACKAGE" ]; then
bundle install --path target/vendor/bundle
pushd target
fpm -s gem -t deb ruby_package/jam_db-$VERSION.gem
find vendor/bundle/ruby/2.2.0/cache -name '*.gem' | xargs -rn1 fpm -s gem -t deb
find vendor/bundle/ruby/2.0.0/cache -name '*.gem' | xargs -rn1 fpm -s gem -t deb
popd
fi

View File

@ -1,7 +1,7 @@
#!/bin/bash
GEM_SERVER=http://localhost:9000/gems
DEB_SERVER=http://localhost:9010/apt-`uname -p`
GEM_SERVER=https://int.jamkazam.com:9000/gems
DEB_SERVER=https://int.jamkazam.com:9010/apt-`uname -p`
echo "starting build..."
./build

View File

@ -276,12 +276,6 @@ jam_track_duration.sql
sales.sql
show_whats_next_count.sql
recurly_adjustments.sql
alter_type_columns.sql
user_presences_rename.sql
add_genre_type.sql
add_description_to_perf_samples.sql
alter_genre_player_unique_constraint.sql
musician_search.sql
signup_hints.sql
packaging_notices.sql
first_played_jamtrack_at.sql
@ -292,6 +286,17 @@ signing.sql
optimized_redeemption.sql
optimized_redemption_warn_mode.sql
affiliate_partners2.sql
enhance_band_profile.sql
broadcast_notifications.sql
broadcast_notifications_fk.sql
calendar.sql
alter_type_columns.sql
user_presences_rename.sql
add_genre_type.sql
add_description_to_perf_samples.sql
alter_genre_player_unique_constraint.sql
musician_search.sql
enhance_band_profile.sql
alter_band_profile_rate_defaults.sql
repair_band_profile.sql
jam_track_onboarding_enhancements.sql
jam_track_name_drop_unique.sql

View File

@ -0,0 +1,2 @@
ALTER TABLE bands ALTER COLUMN hourly_rate SET DEFAULT NULL;
ALTER TABLE bands ALTER COLUMN gig_minimum SET DEFAULT NULL;

13
db/up/calendar.sql Normal file
View File

@ -0,0 +1,13 @@
CREATE TABLE calendars (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL,
user_id VARCHAR(64) NOT NULL REFERENCES users(id) ON DELETE CASCADE,
target_uid VARCHAR(64) NOT NULL,
name VARCHAR(128),
description VARCHAR(8000),
trigger_delete BOOLEAN DEFAULT FALSE,
start_at TIMESTAMP WITHOUT TIME ZONE NOT NULL,
end_at TIMESTAMP WITHOUT TIME ZONE NOT NULL,
recurring_mode VARCHAR(50) NOT NULL DEFAULT 'once',
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL,
updated_at TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW() NOT NULL
);

View File

@ -0,0 +1 @@
ALTER TABLE jam_tracks DROP CONSTRAINT jam_tracks_name_key;

View File

@ -0,0 +1,78 @@
-- "rsvp_slots_instrument_id_fkey" FOREIGN KEY (instrument_id) REFERENCES instruments(id)
-- "musicians_instruments_instrument_id_fkey" FOREIGN KEY (instrument_id) REFERENCES instruments(id) ON DELETE CASCADE
-- "saved_tracks_instrument_id_fkey" FOREIGN KEY (instrument_id) REFERENCES instruments(id) ON DELETE CASCADE
ALTER TABLE rsvp_slots DROP CONSTRAINT rsvp_slots_instrument_id_fkey;
ALTER TABLE musicians_instruments DROP CONSTRAINT musicians_instruments_instrument_id_fkey;
ALTER TABLE recorded_tracks DROP CONSTRAINT saved_tracks_instrument_id_fkey;
UPDATE instruments SET id = 'double bass', description = 'Double Bass' WHERE id = 'upright bass';
UPDATE rsvp_slots SET instrument_id = 'double bass' where instrument_id = 'upright bass';
UPDATE musicians_instruments SET instrument_id = 'double bass' where instrument_id = 'upright bass';
UPDATE recorded_tracks SET instrument_id = 'double bass' where instrument_id = 'upright bass';
ALTER TABLE rsvp_slots ADD CONSTRAINT rsvp_slots_instrument_id_fkey FOREIGN KEY (instrument_id) REFERENCES instruments(id) ON DELETE SET NULL;
ALTER TABLE musicians_instruments ADD CONSTRAINT musicians_instruments_instrument_id_fkey FOREIGN KEY (instrument_id) REFERENCES instruments(id) ON DELETE CASCADE;
ALTER TABLE recorded_tracks ADD CONSTRAINT saved_tracks_instrument_id_fkey FOREIGN KEY (instrument_id) REFERENCES instruments(id) ON DELETE CASCADE;
INSERT INTO instruments (id, description, popularity) VALUES ('steel guitar', 'Steel Guitar', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('orchestra', 'Orchestra', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('glockenspiel', 'Glockenspiel', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('dobro', 'Dobro', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('harp', 'Harp', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('vocoder', 'Vocoder', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('flugelhorn', 'Flugelhorn', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('timpani', 'Timpani', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('bassoon', 'Bassoon', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('charango', 'Charango', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('theremin', 'Theremin', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('sitar', 'Sitar', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('piccolo', 'Piccolo', 1);
INSERT INTO instruments (id, description, popularity) VALUES ('bagpipes', 'Bagpipes', 1);
ALTER TABLE jam_tracks ADD COLUMN onboarding_exceptions JSON;
ALTER TABLE jam_track_tracks ADD COLUMN original_filename VARCHAR;
ALTER TABLE jam_tracks ADD COLUMN additional_info VARCHAR;
ALTER TABLE jam_tracks ADD COLUMN language VARCHAR NOT NULL DEFAULT 'eng';
ALTER TABLE jam_tracks ADD COLUMN year INTEGER;
ALTER TABLE jam_tracks ADD COLUMN vendor_id VARCHAR;
INSERT INTO jam_track_licensors (name, description) VALUES ('Tency Music', 'Tency Music is a music production company specialized in re-recordings.');
CREATE TABLE genres_jam_tracks (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
jam_track_id VARCHAR(64) NOT NULL REFERENCES jam_tracks(id) ON DELETE CASCADE,
genre_id VARCHAR(64) NOT NULL REFERENCES genres(id) ON DELETE CASCADE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO genres_jam_tracks (jam_track_id, genre_id) ((SELECT jam_tracks.id, jam_tracks.genre_id FROM jam_tracks));
ALTER TABLE jam_tracks DROP COLUMN genre_id;
-- holds precount, click.wav, click.txt
CREATE TABLE jam_track_files (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
jam_track_id VARCHAR(64) REFERENCES jam_tracks(id) ON DELETE CASCADE,
file_type VARCHAR NOT NULL,
original_filename VARCHAR NOT NULL,
precount_num INTEGER,
url VARCHAR,
md5 VARCHAR,
length bigint,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO genres (id, description) VALUES ('soft rock', 'Soft Rock');
INSERT INTO genres (id, description) VALUES ('rap', 'Rap');
INSERT INTO genres (id, description) VALUES ('tv & movie soundtrack', 'TV & Movie Soundtrack');
INSERT INTO genres (id, description) VALUES ('holiday', 'Holiday');
INSERT INTO genres (id, description) VALUES ('kids', 'Kids');
INSERT INTO genres (id, description) VALUES ('disco', 'Disco');
INSERT INTO genres (id, description) VALUES ('soul', 'Soul');
INSERT INTO genres (id, description) VALUES ('hard rock', 'Hard Rock');
INSERT INTO genres (id, description) VALUES ('funk', 'Funk');
INSERT INTO genres (id, description) VALUES ('dance', 'Dance');
INSERT INTO genres (id, description) VALUES ('creole', 'Creole');
INSERT INTO genres (id, description) VALUES ('traditional', 'Traditional');
INSERT INTO genres (id, description) VALUES ('oldies', 'Oldies');
INSERT INTO genres (id, description) VALUES ('world', 'World');
INSERT INTO genres (id, description) VALUES ('musical', 'Musical');
INSERT INTO genres (id, description) VALUES ('celtic', 'Celtic');

View File

@ -0,0 +1,3 @@
ALTER TABLE musicians_instruments ALTER COLUMN player_id SET NOT NULL;
ALTER TABLE performance_samples ALTER COLUMN player_id SET NOT NULL;
ALTER TABLE online_presences ALTER COLUMN player_id SET NOT NULL;

View File

@ -0,0 +1,2 @@
ALTER TABLE users ALTER paid_sessions_hourly_rate TYPE integer;
ALTER TABLE users ALTER paid_sessions_daily_rate TYPE integer;

View File

@ -1,6 +1,6 @@
#!/bin/bash
GEM_SERVER=http://localhost:9000/gems
GEM_SERVER=https://int.jamkazam.com:9000/gems
echo "starting build..."
./build

1
ruby/.gitignore vendored
View File

@ -9,6 +9,7 @@ _yardoc
coverage
doc/
lib/bundler/man
jt_metadata.json
pkg
rdoc
spec/reports

View File

@ -18,15 +18,15 @@ end
gem 'pg', '0.17.1', :platform => [:mri, :mswin, :mingw]
gem 'jdbc_postgres', :platform => [:jruby]
gem 'activerecord', '3.2.13'
gem 'activerecord', '3.2.22'
gem "activerecord-import", "~> 0.4.1"
gem 'uuidtools', '2.1.2'
gem 'bcrypt-ruby', '3.0.1'
gem 'ruby-protocol-buffers', '1.2.2'
gem 'eventmachine', '1.0.3'
gem 'eventmachine', '1.0.4'
gem 'amqp', '1.0.2'
gem 'will_paginate'
gem 'actionmailer', '3.2.13'
gem 'actionmailer', '3.2.22'
gem 'sendgrid', '1.2.0'
gem 'aws-sdk', '~> 1'
gem 'carrierwave', '0.9.0'
@ -64,6 +64,7 @@ group :test do
gem 'rspec-prof'
gem 'time_difference'
gem 'byebug'
gem 'icalendar'
end
# Specify your gem's dependencies in jam_ruby.gemspec

View File

@ -1,6 +1,6 @@
#!/bin/bash
GEM_SERVER=http://localhost:9000/gems
GEM_SERVER=https://int.jamkazam.com:9000/gems
echo "starting build..."
./build

View File

@ -1 +0,0 @@
{"container_file": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150519-97259-1h1tbhj/jam-track-35.jkz", "version": "0", "coverart": null, "rsa_priv_file": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150519-97259-1h1tbhj/skey.pem", "tracks": [{"name": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150519-97259-1h1tbhj/7452fa4a-0c55-4cb2-948e-221475d7299c.ogg", "trackName": "track_00"}], "rsa_pub_file": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/d20150519-97259-1h1tbhj/pkey.pem", "jamktrack_info": "/var/folders/fk/0ckzmddd4tq28kxbb09vckbr0000gn/T/tmpGdncJS"}

View File

@ -51,8 +51,10 @@ 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/scheduled/daily_job"
require "jam_ruby/resque/scheduled/daily_session_emailer"
require "jam_ruby/resque/scheduled/new_musician_emailer"
require "jam_ruby/resque/scheduled/music_session_reminder"
require "jam_ruby/resque/scheduled/music_session_scheduler"
require "jam_ruby/resque/scheduled/active_music_session_cleaner"
require "jam_ruby/resque/scheduled/score_history_sweeper"
@ -94,6 +96,7 @@ require "jam_ruby/amqp/amqp_connection_manager"
require "jam_ruby/database"
require "jam_ruby/message_factory"
require "jam_ruby/models/backing_track"
require "jam_ruby/models/calendar"
require "jam_ruby/models/feedback"
require "jam_ruby/models/feedback_observer"
#require "jam_ruby/models/max_mind_geo"
@ -203,6 +206,8 @@ require "jam_ruby/models/jam_track"
require "jam_ruby/models/jam_track_track"
require "jam_ruby/models/jam_track_right"
require "jam_ruby/models/jam_track_tap_in"
require "jam_ruby/models/jam_track_file"
require "jam_ruby/models/genre_jam_track"
require "jam_ruby/app/mailers/async_mailer"
require "jam_ruby/app/mailers/batch_mailer"
require "jam_ruby/app/mailers/progress_mailer"
@ -227,6 +232,7 @@ require "jam_ruby/models/sale_line_item"
require "jam_ruby/models/recurly_transaction_web_hook"
require "jam_ruby/models/broadcast_notification"
require "jam_ruby/models/broadcast_notification_view"
require "jam_ruby/calendar_manager"
require "jam_ruby/jam_tracks_manager"
require "jam_ruby/jam_track_importer"
require "jam_ruby/jmep_manager"
@ -236,6 +242,7 @@ require "jam_ruby/models/json_store"
require "jam_ruby/models/base_search"
require "jam_ruby/models/musician_search"
require "jam_ruby/models/band_search"
require "jam_ruby/import/tency_stem_mapping"
include Jampb

View File

@ -390,13 +390,23 @@
end
end
def scheduled_session_reminder(user, msg, session)
def scheduled_session_reminder_upcoming(user, session)
subject = "Your JamKazam session starts in 1 hour!"
unique_args = {:type => "scheduled_session_reminder_upcoming"}
send_scheduled_session_reminder(user, session, subject, unique_args)
end
def scheduled_session_reminder_day(user, session)
subject = "JamKazam Session Reminder"
unique_args = {:type => "scheduled_session_reminder_day"}
send_scheduled_session_reminder(user, session, subject, unique_args)
end
def send_scheduled_session_reminder(user, session, subject, unique_args)
return if !user.subscribe_email
email = user.email
subject = "Session Rescheduled"
unique_args = {:type => "scheduled_session_reminder"}
@body = msg
@user = user
@session_name = session.name
@session_date = session.pretty_scheduled_start(true)
@session_url = "#{APP_CONFIG.external_root_url}/sessions/#{session.id}/details"

View File

@ -1,10 +0,0 @@
<% provide(:title, 'Scheduled Session Reminder') %>
<p><%= @body %></p>
<p>
<%= @session_name %><br/>
<%= @session_date %>
</p>
<p><a style="color: #ffcc00;" href="<%= @session_url %>">View Session Details</a></p>

View File

@ -1,6 +0,0 @@
<%= @body %>
<%= @session_name %>
<%= @session_date %>
See session details at <%= @session_url %>.

View File

@ -0,0 +1,18 @@
<% provide(:title, 'JamKazam Session Reminder') %>
<div>
Hi <%= @user.first_name %>,
</div>
<br/>
<div>
<span>This is a reminder that your JamKazam session</span>
<a href='<%=@session_url%>'><%= @session_name %></a>
<span>is scheduled for tomorrow. We hope you have fun!</span>
</div>
<br/>
<div>
Best Regards,
<br/>
Team JamKazam
</div>

View File

@ -0,0 +1,8 @@
Hi <%= @user.first_name %>,
This is a reminder that your JamKazam session <%=@session_name%> is scheduled for tomorrow. We hope you have fun!
Best Regards,
Team JamKazam
See session details at <%= @session_url %>.

View File

@ -0,0 +1,17 @@
<% provide(:title, 'Your JamKazam session starts in 1 hour!') %>
<div>
Hi <%= @user.first_name %>,
</div>
<br/>
<div>
<span>This is a reminder that your JamKazam session</span>
<a href='<%=@session_url%>'><%= @session_name %></a>
<span>starts in 1 hour. We hope you have fun!</span>
</div>
<br/>
<div>
Best Regards,
<br/>
Team JamKazam
</div>

View File

@ -0,0 +1,10 @@
Hi <%= @user.first_name %>,
This is a reminder that your JamKazam session
<%=@session_name%>
starts in 1 hour. We hope you have fun!
Best Regards,
Team JamKazam
See session details at <%= @session_url %>.

View File

@ -0,0 +1,106 @@
module JamRuby
class CalendarManager < BaseManager
DATE_FORMAT="%Y%m%dT%H%M%SZ"
def initialize(options={})
super(options)
@log = Logging.logger[self]
end
def cancel_ics_event(music_session, user)
Calendar.where(
user_id: user.id,
target_uid: music_session.id,
name: music_session.description)
.first_or_create(
description: music_session.description,
start_at: music_session.scheduled_start,
end_at: music_session.scheduled_start+music_session.safe_scheduled_duration,
trigger_delete: true)
end
# Remove all "delete" event calendar records older than 4 weeks:
def cleanup()
Calendar.where("trigger_delete=TRUE AND created_at < ?", 4.weeks.ago)
.destroy_all()
end
# @return event (as ICS string) for a given music session
def ics_event_from_music_session(music_session, delete=false)
# Determine properties of calendar event and create:
uid = "#{music_session.id}@JamKazam"
text = "JamKazam Session #{music_session.description}"
rrule = nil
start_at = music_session.scheduled_start
stop_at = music_session.scheduled_start+music_session.safe_scheduled_duration
if !delete && music_session.recurring_mode==MusicSession::RECURRING_WEEKLY
rrule = "FREQ=WEEKLY;INTERVAL=1"
end
create_ics_event(uid, text, text, start_at, stop_at, delete, rrule)
end
# @return event (as ICS string) for a given music session
def ics_event_from_calendar(calendar)
# Determine properties of calendar event and create:
rrule = nil
if !calendar.trigger_delete && calendar.recurring_mode==MusicSession::RECURRING_WEEKLY
rrule = "FREQ=WEEKLY;INTERVAL=1"
end
create_ics_event(
calendar.target_uid,
"JamKazam Session #{calendar.name}",
calendar.description,
calendar.start_at,
calendar.end_at,
calendar.trigger_delete,
rrule
)
end
# @return calendar (as ICS string) for specified user
# Includes all RSVPed sessions, as well as any calendar
# entries for the given user:
def create_ics_feed(user)
ics_events = ""
MusicSession.scheduled_rsvp(user, true).each do |music_session|
ics_events << "\r\n" if(ics_events.length != 0)
ics_events << ics_event_from_music_session(music_session)
end
user.calendars.each do |user|
ics_events << "\r\n" if(ics_events.length != 0)
ics_events << ics_event_from_calendar(user)
end
create_ics_cal(ics_events)
end
# @return event (as ICS string) for given arguments
def create_ics_event(uuid, name, description, start_at, end_at, delete=false, rrule=nil, sequence=nil)
uuid ||= UUID.timestamp_create
event = "BEGIN:VEVENT\r\n"
event << "UID:#{uuid}\r\n"
event << "DTSTAMP:#{Time.now.utc().strftime(DATE_FORMAT)}\r\n"
event << "DTSTART:#{start_at.utc().strftime(DATE_FORMAT)}\r\n"
event << "DTEND:#{end_at.utc().strftime(DATE_FORMAT)}\r\n"
event << "SUMMARY:#{name}\r\n"
event << "DESCRIPTION:#{description}\r\n"
if delete
event << "METHOD:CANCEL\r\n"
event << "STATUS:CANCELLED\r\n"
end
if rrule
event << "RRULE:#{rrule}\r\n"
end
event << "SEQUENCE:#{sequence}\r\n" if sequence
event << "END:VEVENT"
end
# @return calendar (as ICS string) for specified events
def create_ics_cal(ics_events)
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:JamKazam\r\n#{ics_events}\r\nEND:VCALENDAR"
end
end # class
end # module

View File

@ -26,7 +26,9 @@ module NotificationTypes
SCHEDULED_SESSION_RSVP_CANCELLED_ORG = "SCHEDULED_SESSION_RSVP_CANCELLED_ORG"
SCHEDULED_SESSION_CANCELLED = "SCHEDULED_SESSION_CANCELLED"
SCHEDULED_SESSION_RESCHEDULED = "SCHEDULED_SESSION_RESCHEDULED"
SCHEDULED_SESSION_REMINDER = "SCHEDULED_SESSION_REMINDER"
SCHEDULED_SESSION_REMINDER_DAY = "SCHEDULED_SESSION_REMINDER_DAY"
SCHEDULED_SESSION_REMINDER_UPCOMING = "SCHEDULED_SESSION_REMINDER_UPCOMING"
SCHEDULED_SESSION_REMINDER_IMMINENT = "SCHEDULED_SESSION_REMINDER_IMMINENT"
SCHEDULED_SESSION_COMMENT = "SCHEDULED_SESSION_COMMENT"
# recording notifications

View File

@ -0,0 +1,360 @@
module JamRuby
# this is probably a one-off class used to map Tency-named stems into JamKazam-named stems
class TencyStemMapping
@@log = Logging.logger[TencyStemMapping]
def s3_manager
@s3_manager ||= S3Manager.new('jamkazam-tency', APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
end
def initialize
@originals_folder = "/Volumes/sethcall/Dropbox/seth@jamkazam.com/JamTracks - Tency Music - Original Folder for Normalization Map"
@mapping_folder = "/Volumes/sethcall/Dropbox/seth@jamkazam.com/JamTracks - Tency Music"
@original_songs = {}
@mapping_songs = {}
@mappings = {}
end
def create_map
tency_originals
tency_maps
dump
end
def create_mapping_map
tency_maps
dump_map
end
def hydrate
@original_songs = YAML.load_file('original_songs.yml')
@mapping_songs = YAML.load_file('mapping_songs.yml')
end
def parse_sanitized_filename(filename)
instrument = nil
part = nil
basename = File.basename(filename)
stem = basename.index('Stem')
if stem
stripped = basename[(stem + 'Stem'.length)..-5] # takes of 'stem' and '.wav'
stripped.strip!
dash = stripped.index('-')
if dash == 0
stripped = stripped[1..-1].strip!
# now we should have something like "Vocal - Lead" (instrument - part)
instrument, part = stripped.split('-')
instrument.strip! if instrument
part.strip! if part
else
"no or misplaced dash for #{filename}"
end
else
raise "no stem for #{filename}"
end
[instrument, part]
end
# For all the tracks that I have labeled manually as
# Instrument = Upright Bass and Part = Upright Bass,
# can you please change both the Instrument and Part to Double Bass instead?
#
def check_mappings
missing_instrument = 0
missing_part = 0
part_names = []
hydrate
@mapping_songs.each do |cache_id, data|
mapped_filename = data[:filename]
@@log.debug("parsing #{mapped_filename}")
instrument, part = parse_sanitized_filename(mapped_filename)
@@log.debug("parsed #{instrument} (#{part})")
missing_instrument = missing_instrument + 1 unless instrument
missing_part = missing_part + 1 unless part
part_names << mapped_filename unless part
end
@@log.info("SUMMARY")
@@log.info("-------")
@@log.info("missing instruments:#{missing_instrument} missing parts: #{missing_part}")
@@log.info("files with no parts: #{part_names}")
# files with no parts:
# ["Huey Lewis And The News - Heart And Soul - 31957/Heart And Soul Stem - Synth 2.wav",
# "ZZ Top - Tush - 20852/Tush Stem - Clicktrack.wav",
# "Crosby Stills And Nash - Teach Your Children - 15440/Teach Your Children Stem - Bass Guitar.wav",
# /Brad Paisley - She's Everything - 19886/She's Everything Stem - Clicktrack.wav",
# "Toby Keith - Beer For My Horses - 7221/Beer For My Horses Stem - Lap Steel.wav",
# Toby Keith - Beer For My Horses - 7221/Beer For My Horses Stem - Acoustic Guitar.wav"
end
def track_mapping(basename, instr_part)
instrument = instr_part[:instrument]
part = instr_part[:part]
basename.downcase!
info = @mappings[basename]
unless info
info = {matches:[]}
@mappings[basename] = info
end
info[:matches] << instr_part
end
def correlate
mapped = 0
unmapped = 0
unmapped_details = []
no_instrument = []
common_unknown_instruments = {}
hydrate
@mapping_songs.each do |cache_id, data|
# go through each track hand-mapped, and find it's matching song if any.
mapped_filename = data[:filename]
found_original = @original_songs[cache_id]
if found_original
# mapping made
original_filename = found_original[:filename]
original_basename = File.basename(original_filename).downcase
mapped = mapped + 1
instrument, part = parse_sanitized_filename(mapped_filename)
instr_part = JamTrackImporter.determine_instrument(instrument, part)
instr_part[:instrument]
if instr_part[:instrument]
# track the mapping of this one
track_mapping(original_basename, instr_part)
else
@@log.error("unable to determine instrument for #{File.basename(mapped_filename)}")
no_instrument << ({filename: File.basename(mapped_filename), instrument: instrument, part: part})
common_unknown_instruments["#{instrument}-(#{part})"] = 1
end
else
unmapped = unmapped + 1
unmapped_details << {filename: mapped_filename}
end
end
puts("SUMMARY")
puts("-------")
puts("MAPPED:#{mapped} UNMAPPED:#{unmapped}")
unmapped_details.each do |unmapped_detail|
puts "UNMAPPED FILE: #{File.basename(unmapped_detail[:filename])}"
end
puts("UNKNOWN INSTRUMENT: #{no_instrument.length}")
no_instrument.each do |item|
puts("UNKNOWN INSTRUMENT: #{item[:filename]}")
end
common_unknown_instruments.each do |key, value|
puts("#{key}")
end
@mappings.each do |basename, mapping|
matches = mapping[:matches]
counts = matches.each_with_object(Hash.new(0)) { |word,counts| counts[word] += 1 }
ordered_matches = counts.sort_by {|k, v| -v}
output = ""
ordered_matches.each do |match|
detail = match[0]
count = match[1]
output << "#{detail[:instrument]}(#{detail[:part]})/#{count}, "
end
puts "map detail: #{basename}: #{output}"
mapping[:ordered] = ordered_matches
mapping[:detail] = output
end
CSV.open("mapping.csv", "wb") do |csv|
@mappings.each do |basename, mapping|
item = mapping[:ordered]
trust_worthy = item.length == 1
unless trust_worthy
# if the 1st item is at least 4 'counts' more than the next item, we can consider it trust_worthy
if item[0][1] - 4 > item[1][1]
trust_worthy = true
end
end
csv << [ basename, item[0][0][:instrument], item[0][0][:part], item[0][1], trust_worthy ]
end
end
CSV.open("determinate-single-matches.csv", "wb") do |csv|
@mappings.each do |basename, mapping|
if mapping[:ordered].length == 1 && mapping[:ordered][0][1] == 1
item = mapping[:ordered]
csv << [ basename, item[0][0][:instrument], item[0][0][:part], item[0][1] ]
end
end
end
CSV.open("determinate-multi-matches.csv", "wb") do |csv|
@mappings.each do |basename, mapping|
if mapping[:ordered].length == 1 && mapping[:ordered][0][1] > 1
item = mapping[:ordered]
csv << [ basename, item[0][0][:instrument], item[0][0][:part], item[0][1] ]
end
end
end
CSV.open("ambiguous-matches.csv", "wb") do |csv|
@mappings.each do |basename, mapping|
if mapping[:ordered].length > 1
csv << [ basename, mapping[:detail] ]
end
end
end
end
def dump
File.open('original_songs.yml', 'w') {|f| f.write(YAML.dump(@original_songs)) }
File.open('mapping_songs.yml', 'w') {|f| f.write(YAML.dump(@mapping_songs)) }
end
def dump_map
File.open('mapping_songs.yml', 'w') {|f| f.write(YAML.dump(@mapping_songs)) }
end
def md5(filepath)
Digest::MD5.file(filepath).hexdigest
end
def tency_original_check
songs = Pathname.new(@originals_folder).children.select { |c| c.directory? }
songs.each do |song|
dirs = Pathname.new(song).children.select {|c| c.directory? }
@@log.debug "SONG #{song}"
dirs.each do |dir|
@@log.debug "#{dir.basename.to_s}"
end
@@log.debug ""
end
end
def tency_originals
songs = Pathname.new(@originals_folder).children.select { |c| c.directory? }
songs.each do |filename|
id = parse_id(filename.basename.to_s )
files = Pathname.new(filename).children.select {|c| c.file? }
# also look into any 1st level folders we might find
dirs = Pathname.new(filename).children.select {|c| c.directory? }
dirs.each do |dir|
more_tracks = Pathname.new(dir).children.select {|c| c.file? }
files = files + more_tracks
end
files.each do |file|
@@log.debug("processing original track #{file.to_s}")
md5 = md5(file.to_s)
song = {md5:md5, filename:file.to_s, id:id}
@original_songs[cache_id(id, md5)] = song
end
end
end
def tency_maps
songs = Pathname.new(@mapping_folder).children.select { |c| c.directory? }
songs.each do |song_filename|
id = parse_id_mapped(song_filename.basename.to_s )
@@log.debug "processing song #{song_filename.to_s}"
tracks = Pathname.new(song_filename).children.select {|c| c.file? }
tracks.each do |track|
if track.to_s.include? "Stem"
@@log.debug("processing mapped track #{track.to_s}")
md5 = md5(track.to_s)
song = {md5:md5, filename:track.to_s}
@mapping_songs[cache_id(id, md5)] = song
end
end
end
end
def cache_id(id, md5)
"#{id}-#{md5}"
end
def parse_id(filename)
#amy-winehouse_you-know-i-m-no-good-feat-ghostface-killah_11767
index = filename.rindex('_')
if index
id = filename[(index + 1)..-1]
if id.end_with?('/')
id = id[0...-1]
end
id = id.to_i
if id == 0
raise "no valid ID in filename: #{filename}"
end
else
raise "no _ in filename: #{filename}"
end
id
end
def parse_id_mapped(filename)
#Flyleaf - I'm So Sick - 15771
index = filename.rindex('-')
if index
id = filename[(index + 1)..-1]
if id.end_with?('/')
id = id[0...-1]
end
id.strip!
id = id.to_i
if id == 0
raise "no valid ID in filename: #{filename}"
end
else
raise "no - in filename: #{filename}"
end
id
end
def tency_originals2
s3_manager.list_directories('mapper').each do |song_folder|
@@log.debug("searching through tency directory. song folder:'#{song_folder}'")
id = parse_id(song_folder)
@@log.debug("ID #{id}")
top_folder = s3_manager.list_directories(song_folder)
end
end
end
end

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@ module JamRuby
class Stats
class << self
attr_accessor :client, :host
attr_accessor :client, :host, :ignore
@@log = Logging.logger[JamRuby::Stats]
end
@ -52,6 +52,7 @@ module JamRuby
end
def self.init(options)
influxdb_database = options[:influxdb_database]
influxdb_username = options[:influxdb_username]
influxdb_password = options[:influxdb_password]
@ -71,12 +72,15 @@ module JamRuby
retry: -1
@host = `hostname`.strip
else
self.ignore = true
@@log.debug("stats client not initiated")
end
end
def self.write(name, data)
return if self.ignore # doing any writes in a test environment cause annoying puts to occur
if @client && data && data.length > 0
data['host'] = @host
data['time'] = Time.now.to_i

View File

@ -23,6 +23,8 @@ module JamRuby
validate :validate_photo_info
validate :require_at_least_one_genre, :unless => :skip_genre_validation
validate :limit_max_genres
validates_numericality_of :hourly_rate, greater_than:0, less_than:100000, :if => :paid_gigs
validates_numericality_of :gig_minimum, greater_than:0, less_than:200000, :if => :paid_gigs
before_save :check_lat_lng
before_save :check_website_url
@ -47,10 +49,10 @@ module JamRuby
has_many :recordings, :class_name => "JamRuby::Recording", :foreign_key => "band_id", dependent: :destroy
# self.id = likable_id in likes table
has_many :likers, :as => :likable, :class_name => "JamRuby::Like", :dependent => :destroy, dependent: :destroy
has_many :likers, :as => :likable, :class_name => "JamRuby::Like", :dependent => :destroy
# self.id = followable_id in follows table
has_many :followers, :as => :followable, :class_name => "JamRuby::Follow", :dependent => :destroy, dependent: :destroy
has_many :followers, :as => :followable, :class_name => "JamRuby::Follow", :dependent => :destroy
# invitations
has_many :invitations, :inverse_of => :band, :class_name => "JamRuby::BandInvitation", :foreign_key => "band_id", dependent: :destroy
@ -193,15 +195,28 @@ module JamRuby
band.photo_url = params[:photo_url] if params.has_key?(:photo_url)
band.logo_url = params[:logo_url] if params.has_key?(:logo_url)
if params.has_key?(:genres) && params[:genres]
band.paid_gigs = params[:paid_gigs] if params.has_key?(:paid_gigs)
band.free_gigs = params[:free_gigs] if params.has_key?(:free_gigs)
band.hourly_rate = (params.has_key?(:hourly_rate) && params[:hourly_rate].to_i > 0) ? params[:hourly_rate] : nil
band.gig_minimum = (params.has_key?(:gig_minimum) && params[:hourly_rate].to_i > 0) ? params[:gig_minimum] : nil
band.add_new_members = params[:add_new_members] if params.has_key?(:add_new_members)
band.touring_option = params[:touring_option] if params.has_key?(:touring_option)
band.band_type = params[:band_type] if params.has_key?(:band_type)
band.band_status = params[:band_status] if params.has_key?(:band_status)
band.concert_count = params[:concert_count] if params.has_key?(:concert_count)
band.play_commitment = params[:play_commitment] if params.has_key?(:play_commitment)
if params[:validate_genres] || params[:genres].present?
# loop through each genre in the array and save to the db
genres = []
params[:genres].each { |genre_id| genres << Genre.find(genre_id) }
params[:genres].each { |genre_id| genres << Genre.find(genre_id) } if params[:genres].present?
band.genres = genres
band.skip_genre_validation = false
else
params[:validate_genres]
band.skip_genre_validation = true
end
band.skip_genre_validation = true unless params[:validate_genres]
unless band.new_record?
OnlinePresence.delete_all(["player_id = ?", band.id])
PerformanceSample.delete_all(["player_id = ?", band.id])

View File

@ -0,0 +1,14 @@
module JamRuby
class Calendar < ActiveRecord::Base
include HtmlSanitize
html_sanitize strict: [:name, :description]
attr_accessible :name, :description, :target_uid, :trigger_delete, :start_at, :end_at
@@log = Logging.logger[Calendar]
self.table_name = "calendars"
self.primary_key = 'id'
belongs_to :user, :class_name => 'JamRuby::User', :foreign_key => :user_id, :inverse_of => :calendars
end
end

View File

@ -200,7 +200,7 @@ SELECT
tmp_candidate_sessions.creator_score_idx AS creator_score_idx
INTO TEMP TABLE tmp_candidate_recipients
FROM users
INNER JOIN musicians_instruments AS mi ON mi.user_id = users.id
INNER JOIN musicians_instruments AS mi ON mi.player_id = users.id
INNER JOIN tmp_candidate_sessions ON tmp_candidate_sessions.is_unstructured_rsvp = TRUE OR
(tmp_candidate_sessions.open_rsvps = TRUE AND tmp_candidate_sessions.instrument_id = mi.instrument_id) OR
tmp_candidate_sessions.invited_user_id = users.id

View File

@ -16,7 +16,8 @@ module JamRuby
has_and_belongs_to_many :recordings, :class_name => "JamRuby::Recording", :join_table => "recordings_genres"
# jam tracks
has_many :jam_tracks, :class_name => "JamRuby::JamTrack"
has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "genre_id"
has_many :jam_tracks, :through => :genres_jam_tracks, :class_name => "JamRuby::JamTrack", :source => :genre
def to_s
description

View File

@ -0,0 +1,8 @@
module JamRuby
class GenreJamTrack < ActiveRecord::Base
self.table_name = 'genres_jam_tracks'
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
belongs_to :genre, class_name: 'JamRuby::Genre'
end
end

View File

@ -14,12 +14,12 @@ module JamRuby
attr_accessor :uploading_preview
attr_accessible :name, :description, :bpm, :time_signature, :status, :recording_type,
:original_artist, :songwriter, :publisher, :licensor, :licensor_id, :pro, :genre, :genre_id, :sales_region, :price,
:original_artist, :songwriter, :publisher, :licensor, :licensor_id, :pro, :genres_jam_tracks_attributes, :sales_region, :price,
:reproduction_royalty, :public_performance_royalty, :reproduction_royalty_amount,
:licensor_royalty_amount, :pro_royalty_amount, :plan_code, :initial_play_silence, :jam_track_tracks_attributes,
:jam_track_tap_ins_attributes, :version, :jmep_json, :jmep_text, :pro_ascap, :pro_bmi, :pro_sesac, :duration, as: :admin
:jam_track_tap_ins_attributes, :genre_ids, :version, :jmep_json, :jmep_text, :pro_ascap, :pro_bmi, :pro_sesac, :duration, as: :admin
validates :name, presence: true, uniqueness: true, length: {maximum: 200}
validates :name, presence: true, length: {maximum: 200}
validates :plan_code, presence: true, uniqueness: true, length: {maximum: 50 }
validates :description, length: {maximum: 1000}
validates :time_signature, inclusion: {in: [nil] + [''] + TIME_SIGNATURES} # the empty string is needed because of activeadmin
@ -39,14 +39,17 @@ module JamRuby
validates :public_performance_royalty, inclusion: {in: [nil, true, false]}
validates :duration, numericality: {only_integer: true}, :allow_nil => true
validates_format_of :reproduction_royalty_amount, with: /^\d+\.*\d{0,3}$/
validates_format_of :licensor_royalty_amount, with: /^\d+\.*\d{0,3}$/
validates_format_of :reproduction_royalty_amount, with: /^\d+\.*\d{0,4}$/, :allow_blank => true
validates_format_of :licensor_royalty_amount, with: /^\d+\.*\d{0,4}$/, :allow_blank => true
belongs_to :genre, class_name: "JamRuby::Genre"
belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id'
belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id', :inverse_of => :jam_tracks
has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "jam_track_id"
has_many :genres, :through => :genres_jam_tracks, :class_name => "JamRuby::Genre", :source => :genre
has_many :jam_track_tracks, :class_name => "JamRuby::JamTrackTrack", order: 'track_type ASC, position ASC, part ASC, instrument_id ASC'
has_many :jam_track_tap_ins, :class_name => "JamRuby::JamTrackTapIn", order: 'offset_time ASC'
has_many :jam_track_files, :class_name => "JamRuby::JamTrackFile"
has_many :jam_track_rights, :class_name => "JamRuby::JamTrackRight" #, inverse_of: 'jam_track', :foreign_key => "jam_track_id" # '
@ -67,6 +70,82 @@ module JamRuby
accepts_nested_attributes_for :jam_track_tracks, allow_destroy: true
accepts_nested_attributes_for :jam_track_tap_ins, allow_destroy: true
# we can make sure a few things stay in sync here.
# 1) the reproduction_royalty_amount has to stay in sync based on duration
# 2) the onboarding_exceptions JSON column
after_save :sync_reproduction_royalty
after_save :sync_onboarding_exceptions
def sync_reproduction_royalty
# reproduction royalty table based on duration
# The statutory mechanical royalty rate for permanent digital downloads is:
# 9.10¢ per copy for songs 5 minutes or less, or
# 1.75¢ per minute or fraction thereof, per copy for songs over 5 minutes.
# So the base rate is 9.1 cents for anything up to 5 minutes.
# 5.01 to 6 minutes should be 10.5 cents.
# 6.01 to 7 minutes should be 12.25 cents.
# Etc.
royalty = nil
if self.duration
minutes = (self.duration - 1) / 60
extra_minutes = minutes - 4
extra_minutes = 0 if extra_minutes < 0
royalty = (0.091 + (0.0175 * extra_minutes)).round(5)
end
self.update_column(:reproduction_royalty_amount, royalty)
true
end
def sync_onboarding_exceptions
exceptions = {}
if self.duration.nil?
exceptions[:no_duration] = true
end
if self.genres.count == 0
exceptions[:no_genres] = true
end
if self.year.nil?
exceptions[:no_year] = true
end
if self.licensor.nil?
exceptions[:no_licensor] = true
end
if self.missing_instrument_info?
exceptions[:unknown_instrument] = true
end
if self.master_track.nil?
exceptions[:no_master] = true
end
if missing_previews?
exceptions[:missing_previews] = true
end
if duplicate_positions?
exceptions[:duplicate_positions] = true
end
if exceptions.keys.length == 0
self.update_column(:onboarding_exceptions, nil)
else
self.update_column(:onboarding_exceptions, exceptions.to_json)
end
true
end
def duplicate_positions?
counter = {}
jam_track_tracks.each do |track|
@ -87,6 +166,17 @@ module JamRuby
duplicate
end
def missing_instrument_info?
missing_instrument_info = false
self.jam_track_tracks.each do |track|
if track.instrument_id == 'other' && (track.part == nil || track.part.start_with?('Other'))
missing_instrument_info = true
break
end
end
missing_instrument_info
end
def missing_previews?
missing_preview = false
self.jam_track_tracks.each do |track|
@ -171,7 +261,7 @@ module JamRuby
end
if options[:group_artist]
query = query.select("original_artist, array_agg(jam_tracks.id) AS id, MIN(name) AS name, MIN(description) AS description, MIN(recording_type) AS recording_type, MIN(original_artist) AS original_artist, MIN(songwriter) AS songwriter, MIN(publisher) AS publisher, MIN(sales_region) AS sales_region, MIN(price) AS price, MIN(version) AS version, MIN(genre_id) AS genre_id")
query = query.select("original_artist, array_agg(jam_tracks.id) AS id, MIN(name) AS name, MIN(description) AS description, MIN(recording_type) AS recording_type, MIN(original_artist) AS original_artist, MIN(songwriter) AS songwriter, MIN(publisher) AS publisher, MIN(sales_region) AS sales_region, MIN(price) AS price, MIN(version) AS version")
query = query.group("original_artist")
query = query.order('jam_tracks.original_artist')
else
@ -180,7 +270,12 @@ module JamRuby
end
query = query.where("jam_tracks.status = ?", 'Production') unless user.admin
query = query.where("jam_tracks.genre_id = '#{options[:genre]}'") unless options[:genre].blank?
unless options[:genre].blank?
query = query.joins(:genres)
query = query.where('genre_id = ? ', options[:genre])
end
query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}' and jam_track_tracks.track_type != 'Master'") unless options[:instrument].blank?
query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?
@ -231,7 +326,12 @@ module JamRuby
query = query.order('jam_tracks.original_artist')
query = query.where("jam_tracks.status = ?", 'Production') unless user.admin
query = query.where("jam_tracks.genre_id = '#{options[:genre]}'") unless options[:genre].blank?
unless options[:genre].blank?
query = query.joins(:genres)
query = query.where('genre_id = ? ', options[:genre])
end
query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}'") unless options[:instrument].blank?
query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?

View File

@ -0,0 +1,78 @@
module JamRuby
# holds a click track or precount file
class JamTrackFile < ActiveRecord::Base
include JamRuby::S3ManagerMixin
# there should only be one Master per JamTrack, but there can be N Track per JamTrack
FILE_TYPE = %w{ClickWav ClickTxt Precount}
@@log = Logging.logger[JamTrackFile]
before_destroy :delete_s3_files
attr_accessible :jam_track_id, :file_type, :filename, as: :admin
attr_accessible :url, :md5, :length, as: :admin
attr_accessor :original_audio_s3_path, :skip_uploader, :preview_generate_error
before_destroy :delete_s3_files
validates :file_type, inclusion: {in: FILE_TYPE }
belongs_to :jam_track, class_name: "JamRuby::JamTrack"
# create storage directory that will house this jam_track, as well as
def store_dir
"jam_track_files"
end
# create name of the file
def filename(original_name)
"#{store_dir}/#{jam_track.original_artist}/#{jam_track.name}/#{original_name}"
end
def manually_uploaded_filename
if click_wav?
filename('click.wav')
elsif click_txt?
filename('click.txt')
elsif precount?
filename('precount.wav')
else
raise 'unknown file type: ' + file_type
end
end
def click_wav?
track_type == 'ClickWav'
end
def click_txt?
track_type == 'ClickTxt'
end
def precount?
track_type == 'Precount'
end
# creates a short-lived URL that has access to the object.
# the idea is that this is used when a user who has the rights to this tries to download this JamTrack
# we would verify their rights (can_download?), and generates a URL in response to the click so that they can download
# but the url is short lived enough so that it wouldn't be easily shared
def sign_url(expiration_time = 120)
s3_manager.sign_url(self[url], {:expires => expiration_time, :response_content_type => 'audio/wav', :secure => true})
end
def can_download?(user)
# I think we have to make a special case for 'previews', but maybe that's just up to the controller to not check can_download?
jam_track.owners.include?(user)
end
def delete_s3_files
s3_manager.delete(self[:url]) if self[:url] && s3_manager.exists?(self[:url])
end
end
end

View File

@ -1,6 +1,8 @@
module JamRuby
class JamTrackLicensor < ActiveRecord::Base
table_name = 'jam_track_licensors'
attr_accessible :name, :description, :attention, :address_line_1, :address_line_2,
:city, :state, :zip_code, :contact, :email, :phone, as: :admin
@ -16,6 +18,6 @@ module JamRuby
validates :email, length: {maximum: 200}
validates :phone, length: {maximum: 200}
has_many :jam_tracks, :class_name => "JamRuby::JamTrack", foreign_key: 'licensor_id'
has_many :jam_tracks, :class_name => "JamRuby::JamTrack", foreign_key: 'licensor_id', :inverse_of => :licensor
end
end

View File

@ -131,22 +131,36 @@ module JamRuby
end
def generate_preview
begin
Dir.mktmpdir do |tmp_dir|
input = File.join(tmp_dir, 'in.ogg')
output = File.join(tmp_dir, 'out.ogg')
output_mp3 = File.join(tmp_dir, 'out.mp3')
start = self.preview_start_time.to_f / 1000
stop = start + 20
raise 'no track' unless self["url_44"]
s3_manager.download(self.url_by_sample_rate(44), input)
process_preview(input, tmp_dir)
end
rescue Exception => e
@@log.error("error in sox command #{e.to_s}")
@preview_generate_error = e.to_s
end
end
# input is the original ogg file for the track. tmp_dir is where this code can safely generate output stuff and have it cleaned up later
def process_preview(input, tmp_dir)
uuid = SecureRandom.uuid
output = File.join(tmp_dir, "#{uuid}.ogg")
output_mp3 = File.join(tmp_dir, "#{uuid}.mp3")
start = self.preview_start_time.to_f / 1000
stop = start + 20
command = "sox \"#{input}\" \"#{output}\" trim #{sprintf("%.3f", start)} =#{sprintf("%.3f", stop)}"
@@log.debug("trimming using: " + command)
@ -207,12 +221,6 @@ module JamRuby
end
end
end
rescue Exception => e
@@log.error("error in sox command #{e.to_s}")
@preview_generate_error = e.to_s
end
end
private

View File

@ -44,10 +44,10 @@ module JamRuby
has_one :share_token, :class_name => "JamRuby::ShareToken", :inverse_of => :shareable, :foreign_key => 'shareable_id'
has_one :feed, :class_name => "JamRuby::Feed", :inverse_of => :music_session, :foreign_key => 'music_session_id', :dependent => :destroy
belongs_to :genre, :class_name => "JamRuby::Genre", :inverse_of => :music_sessions, :foreign_key => 'genre_id'
has_many :join_requests, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::JoinRequest", :foreign_key => "music_session_id"
has_many :invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::Invitation", :foreign_key => "music_session_id"
has_many :join_requests, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::JoinRequest"
has_many :invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::Invitation"
has_many :invited_musicians, :through => :invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver
has_many :fan_invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::FanInvitation", :foreign_key => "music_session_id"
has_many :fan_invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::FanInvitation"
has_many :invited_fans, :through => :fan_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver
has_many :rsvp_slots, :class_name => "JamRuby::RsvpSlot", :foreign_key => "music_session_id", :dependent => :destroy
has_many :music_notations, :class_name => "JamRuby::MusicNotation", :foreign_key => "music_session_id"
@ -880,6 +880,21 @@ SQL
end
result
end
def safe_scheduled_duration
duration = scheduled_duration
# you can put seconds into the scheduled_duration field, but once stored, it comes back out as a string
if scheduled_duration.class == String
begin
bits = scheduled_duration.split(':')
duration = bits[0].to_i.hours + bits[1].to_i.minutes + bits[2].to_i.seconds
rescue Exception => e
duration = 1.hours
@@log.error("unable to parse duration #{scheduled_duration}")
end
end
duration
end
# should create a timestamp like:
#
# with_timezone = TRUE
@ -910,17 +925,7 @@ SQL
end
end
duration = scheduled_duration
# you can put seconds into the scheduled_duration field, but once stored, it comes back out as a string
if scheduled_duration.class == String
begin
bits = scheduled_duration.split(':')
duration = bits[0].to_i.hours + bits[1].to_i.minutes + bits[2].to_i.seconds
rescue Exception => e
duration = 1.hours
@@log.error("unable to parse duration #{scheduled_duration}")
end
end
duration = safe_scheduled_duration
end_time = start_time + duration
if with_timezone
"#{start_time.strftime("%A, %B %e")}, #{start_time.strftime("%l:%M").strip}-#{end_time.strftime("%l:%M %p").strip} #{timezone_display}"

View File

@ -10,14 +10,8 @@ module JamRuby
attr_accessible :max_concurrent_connections, :session_removed_at, :rating
validates_inclusion_of :rating, :in => -1..1, :allow_nil => true
belongs_to(:user,
:class_name => "JamRuby::User",
:foreign_key => "user_id",
:inverse_of => :music_session_user_histories)
belongs_to(:music_session,
:class_name => "MusicSession",
:foreign_key => "music_session_id")
belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id", :inverse_of => :music_session_user_histories
belongs_to :music_session, :class_name => "MusicSession", :foreign_key => "music_session_id"
def self.latest_history(client_id)
self.where(:client_id => client_id)

View File

@ -246,47 +246,54 @@ module JamRuby
return 'Click search button to look for musicians with similar interests, skill levels, etc.'
end
jj = self.json
str = 'Current Search: '
str += "Sort = #{SORT_ORDERS[json_value(MusicianSearch::KEY_SORT_ORDER)]}"
str = ''
if 0 < (val = jj[KEY_INSTRUMENTS]).length
str += ", Instruments = "
instr_ids = val.collect { |stored_instrument| stored_instrument['id'] }
instrs = Instrument.where(["id IN (?)", instr_ids]).order(:description)
instrs.each_with_index do |ii, idx|
proficiency = val.detect { |stored_instrument| stored_instrument['id'] == ii.id }['level']
str += "#{ii.description} / #{INSTRUMENT_PROFICIENCY[proficiency.to_i]}"
str += ', ' unless idx==(instrs.length-1)
end
end
if (val = jj[KEY_INTERESTS]) != INTEREST_VALS[0]
str += "; Interest = #{INTERESTS[val]}"
str += ", Interest = #{INTERESTS[val]}"
end
if (val = jj[KEY_SKILL].to_i) != SKILL_VALS[0]
str += "; Skill = #{SKILL_LEVELS[val]}"
str += ", Skill = #{SKILL_LEVELS[val]}"
end
if (val = jj[KEY_STUDIOS].to_i) != STUDIO_COUNTS[0]
str += "; Studio Sessions = #{STUDIOS_LABELS[val]}"
str += ", Studio Sessions = #{STUDIOS_LABELS[val]}"
end
if (val = jj[KEY_GIGS].to_i) != GIG_COUNTS[0]
str += "; Concert Gigs = #{GIG_LABELS[val]}"
str += ", Concert Gigs = #{GIG_LABELS[val]}"
end
val = jj[KEY_AGES].map(&:to_i)
val.sort!
if !val.blank?
str += "; Ages = "
str += ", Ages = "
val.each_with_index do |vv, idx|
str += "#{AGES[vv]}"
str += ', ' unless idx==(val.length-1)
end
end
if 0 < (val = jj[KEY_GENRES]).length
str += "; Genres = "
str += ", Genres = "
genres = Genre.where(["id IN (?)", val]).order('description').pluck(:description)
genres.each_with_index do |gg, idx|
str += "#{gg}"
str += ', ' unless idx==(genres.length-1)
end
end
if 0 < (val = jj[KEY_INSTRUMENTS]).length
str += "; Instruments = "
instr_ids = val.collect { |vv| vv['instrument_id'] }
instrs = Instrument.where(["id IN (?)", instr_ids]).order(:description)
instrs.each_with_index do |ii, idx|
proficiency = val.detect { |vv| vv['instrument_id'] == ii.id }['proficiency_level']
str += "#{ii.description} (#{INSTRUMENT_PROFICIENCY[proficiency.to_i]})"
str += ', ' unless idx==(instrs.length-1)
end
str += ", Sort = #{SORT_ORDERS[json_value(MusicianSearch::KEY_SORT_ORDER)]}"
if str.start_with?(', ')
# trim off any leading ,
str = str[2..-1]
end
str = 'Current Search: ' + str
str
end

View File

@ -73,6 +73,10 @@ module JamRuby
@@message_factory = MessageFactory.new
################### HELPERS ###################
def notified?(music_session, notification_type)
Notification.where("session_id=? AND description=?", music_session, notification_type).count != 0
end
def retrieve_friends(connection, user_id)
friend_ids = []
connection.exec("SELECT f.friend_id as friend_id FROM friendships f WHERE f.user_id = $1", [user_id]) do |friend_results|
@ -203,9 +207,15 @@ module JamRuby
when NotificationTypes::SCHEDULED_SESSION_RESCHEDULED
return "The following session has been rescheduled."
when NotificationTypes::SCHEDULED_SESSION_REMINDER
when NotificationTypes::SCHEDULED_SESSION_REMINDER_DAY
return "A session to which you have RSVPd will begin in one hour, so get ready to play!"
when NotificationTypes::SCHEDULED_SESSION_REMINDER_UPCOMING
return "A session to which you have RSVPd will begin in one hour, so get ready to play!"
when NotificationTypes::SCHEDULED_SESSION_REMINDER_IMMINENT
return "A session to which you have RSVPd is scheduled to start in 5 minutes!"
when NotificationTypes::SCHEDULED_SESSION_COMMENT
return "New message about session."
@ -890,33 +900,52 @@ module JamRuby
end
end
def send_scheduled_session_reminder(music_session)
# Send session reminders to sessions that
# start in less than 24 hours, and haven't been
# notified for a particular interval yet:
def send_session_reminders
MusicSession.where("scheduled_start > NOW() AND scheduled_start <= (NOW()+INTERVAL '1 DAYS')").each do |candidate_session|
tm = candidate_session.scheduled_start
if (tm>(12.hours.from_now) && !notified?(candidate_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_DAY))
# Send 24 hour reminders:
send_session_reminder_day(candidate_session)
elsif (tm<=(65.minutes.from_now) && tm>(15.minutes.from_now) && !notified?(candidate_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_UPCOMING))
# Send 1 hour reminders:
send_session_reminder_upcoming(candidate_session)
elsif (tm<=(10.minutes.from_now) && !notified?(candidate_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_IMMINENT))
# Send 5 minute reminders:
send_session_reminder_imminent(candidate_session)
end
end
end
return if music_session.nil?
def send_session_reminder_day(music_session)
send_session_reminder(music_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_DAY) do |music_session, target_user, notification|
begin
UserMailer.scheduled_session_reminder_day(target_user, music_session).deliver
rescue => e
@@log.error("Unable to send SCHEDULED_SESSION_REMINDER_DAY email to user #{target_user.email} #{e}")
end
end
end
rsvp_requests = RsvpRequest.index(music_session)
target_users = rsvp_requests.where(:canceled => false).map { |r| r.user }
# remove the creator from the array
target_users = target_users.uniq - [music_session.creator]
target_users.each do |target_user|
source_user = music_session.creator
notification = Notification.new
notification.description = NotificationTypes::SCHEDULED_SESSION_REMINDER
notification.source_user_id = source_user.id
notification.target_user_id = target_user.id
notification.session_id = music_session.id
notification.save
notification_msg = format_msg(notification.description, {:session => music_session})
def send_session_reminder_upcoming(music_session)
send_session_reminder(music_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_UPCOMING) do |music_session, target_user, notification|
begin
UserMailer.scheduled_session_reminder_upcoming(target_user, music_session).deliver
rescue => e
@@log.error("Unable to send SCHEDULED_SESSION_REMINDER_UPCOMING email to user #{target_user.email} #{e}")
end
end
end
def send_session_reminder_imminent(music_session)
send_session_reminder(music_session, NotificationTypes::SCHEDULED_SESSION_REMINDER_IMMINENT) do |music_session, target_user, notification|
if target_user.online
msg = @@message_factory.scheduled_session_reminder(
target_user.id,
music_session.id,
notification_msg,
format_msg(notification.description, {:session => music_session}),
music_session.name,
music_session.pretty_scheduled_start(false),
notification.id,
@ -925,13 +954,28 @@ module JamRuby
@@mq_router.publish_to_user(target_user.id, msg)
end
begin
UserMailer.scheduled_session_reminder(target_user, notification_msg, music_session).deliver
rescue => e
@@log.error("Unable to send SCHEDULED_SESSION_REMINDER email to user #{target_user.email} #{e}")
end
end
# @param music_session - the session for which to send reminder
# @param reminder_type - the type of reminder; one of:
# => SCHEDULED_SESSION_REMINDER_DAY 24 hours
# => SCHEDULED_SESSION_REMINDER_UPCOMING 15 minutes
# => SCHEDULED_SESSION_REMINDER_IMMINENT 5 minutes (in-app)
def send_session_reminder(music_session, reminder_type)
raise ArgumentError, "Block required" unless block_given?
source_user = music_session.creator
rsvp_requests = RsvpRequest.index(music_session)
rsvp_requests.where(:canceled => false).each do |rsvp|
target_user = rsvp.user
notification = Notification.new
notification.description = reminder_type
notification.source_user_id = source_user.id
notification.target_user_id = target_user.id
notification.session_id = music_session.id
notification.save
yield(music_session, target_user, notification)
end
end
def send_scheduled_session_comment(music_session, creator, comment, send_to_cancelled = false)

View File

@ -8,6 +8,7 @@ module JamRuby
validates :user, presence: true
validates :canceled, :inclusion => {:in => [nil, true, false]}
validate :creator_rsvp_cancel
before_save :cancel_calendar
# pulls all instruments from the associated rsvp_slots
def instrument_list
@ -305,6 +306,15 @@ module JamRuby
errors.add(:canceled, "can't be canceled by the session organizer")
end
end
def cancel_calendar
calendar_manager = CalendarManager.new
if self.canceled
self.rsvp_slots.each do |slot|
calendar_manager.cancel_ics_event(slot.music_session, user)
end
end
end
end
end

View File

@ -18,7 +18,7 @@ module JamRuby
raise "blocidispid must be positive" if blocidispid <= 0
raise "score must be positive" if score <= 0
ascore = Score.create(alocidispid: alocidispid, anodeid: anodeid, aaddr: aaddr, auserid: user_info[:auserid], alatencytestid: user_info[:alatencytestid], blocidispid: blocidispid, bnodeid: bnodeid, baddr: baddr, buserid: user_info[:buserid], blatencytestid: user_info[:blatencytestid], score: score, scorer: 0, score_dt: score_dt, scoring_data: score_data)
bscore = Score.create(alocidispid: blocidispid, anodeid: bnodeid, aaddr: baddr, auserid: user_info[:buserid], blatencytestid: user_info[:blatencytestid], blocidispid: alocidispid, bnodeid: anodeid, baddr: aaddr, buserid: user_info[:auserid], blatencytestid: user_info[:alatencytestid], score: score, scorer: 1, score_dt: score_dt) if alocidispid != blocidispid
bscore = Score.create(alocidispid: blocidispid, anodeid: bnodeid, aaddr: baddr, auserid: user_info[:buserid], alatencytestid: user_info[:blatencytestid], blocidispid: alocidispid, bnodeid: anodeid, baddr: aaddr, buserid: user_info[:auserid], blatencytestid: user_info[:alatencytestid], score: score, scorer: 1, score_dt: score_dt) if alocidispid != blocidispid
Score.connection.execute("select update_current_network_scores(#{alocidispid}, #{blocidispid})")
return [ascore, bscore]
end

View File

@ -45,6 +45,9 @@ module JamRuby
# authorizations (for facebook, etc -- omniauth)
has_many :user_authorizations, :class_name => "JamRuby::UserAuthorization"
# calendars (for scheduling NOT in music_session)
has_many :calendars, :class_name => "JamRuby::Calendar"
# connections (websocket-gateway)
has_many :connections, :class_name => "JamRuby::Connection"
@ -194,6 +197,11 @@ module JamRuby
validates_numericality_of :last_jam_audio_latency, greater_than:MINIMUM_AUDIO_LATENCY, less_than:MAXIMUM_AUDIO_LATENCY, :allow_nil => true
validates :last_jam_updated_reason, :inclusion => {:in => [nil, JAM_REASON_REGISTRATION, JAM_REASON_NETWORK_TEST, JAM_REASON_FTUE, JAM_REASON_JOIN, JAM_REASON_IMPORT, JAM_REASON_LOGIN] }
# stored in cents
validates_numericality_of :paid_sessions_hourly_rate, greater_than:0, less_than:200000, :if => :paid_sessions
# stored in cents
validates_numericality_of :paid_sessions_daily_rate, greater_than:0, less_than:5000000, :if => :paid_sessions
# custom validators
validate :validate_musician_instruments
validate :validate_current_password
@ -699,6 +707,20 @@ module JamRuby
end
end
# Build calendars using given parameter.
# @param calendars (array of hash)
def update_calendars(calendars)
unless self.new_record?
Calendar.where("user_id = ?", self.id).delete_all
end
unless calendars.nil?
calendars.each do |cal|
self.calendars << self.calendars.create(cal)
end
end
end
# given an array of instruments, update a user's instruments
def update_instruments(instruments)
# delete all instruments for this user first

View File

@ -0,0 +1,17 @@
module JamRuby
class DailyJob
extend Resque::Plugins::JamLonelyJob
@queue = :scheduled_daily_job
@@log = Logging.logger[DailyJob]
class << self
def perform
@@log.debug("waking up")
calendar_manager = CalendarManager.new
calendar_manager.cleanup()
@@log.debug("done")
end
end
end
end

View File

@ -0,0 +1,31 @@
require 'json'
require 'resque'
require 'resque-retry'
require 'net/http'
require 'digest/md5'
module JamRuby
class MusicSessionReminder
extend Resque::Plugins::JamLonelyJob
@queue = :music_session_reminder
@@log = Logging.logger[MusicSessionReminder]
def self.lock_timeout
120
end
def self.perform
@@log.debug("MusicSessionReminder waking up")
MusicSessionReminder.new.run
@@log.debug("MusicSessionReminder done")
end
def run
Notification.send_session_reminders()
end
end
end

View File

@ -740,7 +740,7 @@ FactoryGirl.define do
licensor_royalty_amount 0.999
sequence(:plan_code) { |n| "jamtrack-#{n}" }
genre JamRuby::Genre.first
genres [JamRuby::Genre.first]
association :licensor, factory: :jam_track_licensor
factory :jam_track_with_tracks do

View File

@ -0,0 +1,85 @@
require 'spec_helper'
require 'icalendar'
describe CalendarManager do
CALENDAR_NAME="Test Cal"
before :all do
@genre1 = FactoryGirl.create(:genre)
@calendar_manager = JamRuby::CalendarManager.new
# Time resolution is seconds:
@start = Time.at(Time.now.utc.to_i)
@stop =(@start+1.hours)
end
before(:each) do
end
describe "with music sessions" do
before :all do
@creator = FactoryGirl.create(:user)
@music_session = FactoryGirl.create(:music_session, :creator => @creator, :description => CALENDAR_NAME, :genre => @genre1, :scheduled_start=>@start, :scheduled_duration=>3600)
@music_session.reload
end
it "validator detects bad calendar" do
lambda{verify_ical("Bad medicine calendar")}
.should raise_error(RuntimeError)
end
it "can create calendar feed" do
ics = @calendar_manager.create_ics_feed(@creator)
# Basic format checking...there are some online tools that
# check a lot more, but no ruby libs that I could find:
lines = ics.split("\r\n")
lines.should have(12).items
lines.first.should eq("BEGIN:VCALENDAR")
lines.last.should eq("END:VCALENDAR")
lines[-2].should eq("END:VEVENT")
verify_ical(ics)
end
end
describe "with manual calendars" do
before :all do
@creator = FactoryGirl.create(:user)
@creator.calendars<<Calendar.new({:name=>CALENDAR_NAME, :description=>"This is a test", :start_at=>(@start), :end_at=>@stop, :trigger_delete=>false, :target_uid=>"2112"})
end
it "can create calendar feed" do
#pending "foobar"
ics = @calendar_manager.create_ics_feed(@creator)
# Basic format checking...there are some online tools that
# check a lot more, but no ruby libs that I could find:
lines = ics.split("\r\n")
lines.should have(12).items
lines.first.should eq("BEGIN:VCALENDAR")
lines.last.should eq("END:VCALENDAR")
lines[-2].should eq("END:VEVENT")
verify_ical(ics)
end
end
def verify_ical(ics)
strict_parser = Icalendar::Parser.new(ics, true)
cals = strict_parser.parse
cals.should_not be_nil
cals.should have(1).items
cal = cals.first
cal.should_not be_nil
cal.events.should have(1).items
event = cal.events.first
event.should_not be_nil
event.summary.should eq("JamKazam Session #{CALENDAR_NAME}")
event.dtstart.to_i.should_not be_nil
event.dtend.to_i.should_not be_nil
(event.dtstart).to_time.utc.to_i.should eq(@start.to_i)
(event.dtend).to_time.utc.to_i.should eq(@stop.to_i)
end
end

View File

@ -22,11 +22,13 @@ describe JamTrackImporter do
in_directory_with_file(metafile)
before(:each) do
JamTrackImporter.storage_format = 'default'
content_for_file(YAML.dump(sample_yml))
end
it "no meta" do
s3_metalocation = 'audio/Artist 1/Bogus Place/meta.yml'
JamTrackImporter.storage_format = 'default'
JamTrackImporter.load_metalocation(s3_metalocation).should be_nil
end
@ -38,9 +40,105 @@ describe JamTrackImporter do
end
end
describe "sort_tracks" do
let(:jam_track) { FactoryGirl.create(:jam_track) }
let(:importer) { JamTrackImporter.new() }
let(:vocal) {Instrument.find('voice')}
let(:drums) {Instrument.find('drums')}
let(:bass_guitar) {Instrument.find('bass guitar')}
let(:piano) {Instrument.find('piano')}
let(:keyboard) {Instrument.find('keyboard')}
let(:acoustic_guitar) {Instrument.find('acoustic guitar')}
let(:electric_guitar) {Instrument.find('electric guitar')}
let(:other) {Instrument.find('other')}
it "the big sort" do
# specified in https://jamkazam.atlassian.net/browse/VRFS-3296
vocal_lead = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: vocal, part: 'Lead')
vocal_lead_female = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: vocal, part: 'Lead Female')
vocal_lead_male = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: vocal, part: 'Lead Male')
vocal_backing = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: vocal, part: 'Backing')
vocal_random = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: vocal, part: 'Random')
drums_drums = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: drums, part: 'Drums')
drums_percussion = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: drums, part: 'Percussion')
drums_random_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: drums, part: 'A')
drums_random_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: drums, part: 'C')
bass_guitar_bass = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: bass_guitar, part: 'Bass')
bass_guitar_random_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: bass_guitar, part: 'some bass')
bass_guitar_random_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: bass_guitar, part: 'zome bass')
piano_piano = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: piano, part: 'Piano')
keyboard_synth_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: keyboard, part: 'Synth 1')
keyboard_synth_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: keyboard, part: 'Synth 2')
keyboard_pads = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: keyboard, part: 'Pads')
keyboard_random_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: keyboard, part: 'A')
keyboard_random_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: keyboard, part: 'Z')
acoust_guitar_lead = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Lead')
acoust_guitar_lead_x = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Lead X')
acoust_guitar_solo_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Solo 1')
acoust_guitar_solo_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Solo 2')
acoust_guitar_rhythm = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Rhythm')
acoust_guitar_random_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'A')
acoust_guitar_random_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: acoustic_guitar, part: 'Z')
elect_guitar_lead = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Lead')
elect_guitar_lead_x = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Lead X')
elect_guitar_solo_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Solo 1')
elect_guitar_solo_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Solo 2')
elect_guitar_rhythm = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Rhythm')
elect_guitar_random_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'A')
elect_guitar_random_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: electric_guitar, part: 'Z')
other_1 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: other, part: 'Other 1')
other_2 = FactoryGirl.build(:jam_track_track, jam_track: jam_track, instrument: other, part: 'Other 2')
expected = [
vocal_lead,
vocal_lead_female,
vocal_lead_male,
vocal_backing,
vocal_random,
drums_drums,
drums_percussion,
drums_random_1,
drums_random_2,
bass_guitar_bass,
piano_piano,
keyboard_synth_1,
keyboard_synth_2,
keyboard_pads,
keyboard_random_1,
keyboard_random_2,
acoust_guitar_lead,
acoust_guitar_lead_x,
acoust_guitar_rhythm,
acoust_guitar_random_1,
acoust_guitar_solo_1,
acoust_guitar_solo_2,
acoust_guitar_random_2,
elect_guitar_lead,
elect_guitar_lead_x,
elect_guitar_solo_1,
elect_guitar_solo_2,
elect_guitar_rhythm,
elect_guitar_random_1,
elect_guitar_random_2,
bass_guitar_random_1,
bass_guitar_random_2,
other_1,
other_2
]
shuffled = expected.shuffle
sorted_tracks = importer.sort_tracks(shuffled)
importer.set_custom_weight(vocal_lead).should eq(100)
expected.each_with_index do |expected_track, i|
sorted_tracks[i].should eq(expected_track)
end
end
end
describe "synchronize" do
let(:jam_track) { JamTrack.new }
let(:importer) { JamTrackImporter.new }
let(:importer) { JamTrackImporter.new() }
let(:minimum_meta) { nil }
let(:metalocation) { 'audio/Artist 1/Song 1/meta.yml' }
let(:options) {{ skip_audio_upload:true }}
@ -64,7 +162,7 @@ describe JamTrackImporter do
describe "parse_wav" do
it "Guitar" do
result = JamTrackImporter.new.parse_wav('blah/Ready for Love Stem - Guitar - Main.wav')
result = JamTrackImporter.new.parse_file('blah/Ready for Love Stem - Guitar - Main.wav')
result[:instrument].should eq('electric guitar')
result[:part].should eq('Main')
end

View File

@ -15,8 +15,12 @@ describe 'Band Search Model' do
let!(:to_join) { search.search_filter_for_subtype(BandSearch::TO_JOIN) }
let!(:to_hire) { search.search_filter_for_subtype(BandSearch::TO_HIRE) }
before(:each) do
before(:all) do
Recording.delete_all
Band.delete_all
end
before(:each) do
@bands = []
@bands << @band1 = FactoryGirl.create(:band)
@bands << @band2 = FactoryGirl.create(:band)

View File

@ -1,6 +1,6 @@
require 'spec_helper'
describe User do
describe "Band Search" do
let(:user) { FactoryGirl.create(:user) }
let(:band) { FactoryGirl.create(:band, name: "Example Band") }
@ -15,6 +15,11 @@ describe User do
}
}
before(:all) do
Recording.delete_all
Band.delete_all
end
before(:each) do
@user = FactoryGirl.create(:user)
band.touch

View File

@ -19,6 +19,17 @@ describe Band do
}
}
let(:band_params_no_genre) {
{
name: "The Band",
biography: "Biography",
city: 'Austin',
state: 'TX',
country: 'US',
validate_genres:true
}
}
describe 'with instruments' do
it 'builds with instruments' do
band.musician_instruments << FactoryGirl.build(:musician_instrument, player: band)
@ -46,6 +57,9 @@ describe Band do
it "minimum genres" do
new_band.save.should be_false
new_band.errors[:genres].should == [ValidationMessages::BAND_GENRE_MINIMUM_NOT_MET]
new_band.genres = [Genre.first]
new_band.save.should be_true
end
it "maximum genres" do
@ -56,6 +70,22 @@ describe Band do
end
describe "save" do
it "genres validate" do
band=Band.save(user, band_params_no_genre)
band.errors.any?.should be_true
band.errors[:genres].should == [ValidationMessages::BAND_GENRE_MINIMUM_NOT_MET]
band = Band.save(user, band_params)
band.errors.any?.should be_false
# Save again without a genre and make sure we get an error:
p = band_params_no_genre.clone
p[:id] = band.id
band = Band.save(user, p)
band.errors.any?.should be_true
band.errors[:genres].should == [ValidationMessages::BAND_GENRE_MINIMUM_NOT_MET]
end
it "can succeed" do
band = Band.save(user, band_params)
band.errors.any?.should be_false
@ -67,6 +97,33 @@ describe Band do
band.country.should == band_params[:country]
end
it "saves current interests" do
parms = band_params
parms[:paid_gigs]=true
parms[:free_gigs]=false
parms[:hourly_rate]=5000
parms[:gig_minimum]=30000
parms[:add_new_members]=true
parms[:touring_option]=false
parms[:band_type]="virtual"
parms[:band_status]="amateur"
parms[:concert_count]=3
band = Band.save(user, parms)
band.errors.any?.should be_false
band.paid_gigs.should == true
band.free_gigs.should == false
band.hourly_rate.should == 5000
parms[:gig_minimum]=30000
band.add_new_members.should == true
band.touring_option.should == false
band.band_type.should == "virtual"
band.band_status.should == "amateur"
band.concert_count.should == 3
end
it "ensures user is a musician" do
expect{ Band.save(fan, band_params) }.to raise_error("must be a musician")
end

View File

@ -42,8 +42,10 @@ describe BroadcastNotification do
bns = BroadcastNotification.viewable_notifications(user1)
expect(bns.count).to be(3)
expect(bns[0].id).to eq(broadcast3.id)
expect(bns[0].id).to eq(broadcast4.id)
expect(bns.detect {|bb| bb.id==broadcast2.id }).to be_nil
# now view broadcast 4, since it hasn't been seen, which should bring broadcast 3 up as next since it was seen longest ago
broadcast4.did_view(user1)
expect(BroadcastNotification.next_broadcast(user1).id).to eq(broadcast3.id)
end

View File

@ -2,6 +2,13 @@ require 'spec_helper'
describe Feed do
before(:all) do
MusicSession.delete_all
Recording.delete_all
IcecastMount.delete_all
end
let (:user1) { FactoryGirl.create(:user) }
let (:user2) { FactoryGirl.create(:user) }
let (:user3) { FactoryGirl.create(:user) }

View File

@ -12,6 +12,90 @@ describe JamTrack do
jam_track = FactoryGirl.create(:jam_track)
jam_track.licensor.should_not be_nil
jam_track.licensor.jam_tracks.should == [jam_track]
jam_track.genres.length.should eq(1)
end
describe 'sync_reproduction_royalty' do
it "all possible conditions" do
jam_track = FactoryGirl.create(:jam_track)
jam_track.reproduction_royalty_amount.should be_nil
jam_track.duration = 0
jam_track.save!
jam_track.reproduction_royalty_amount.to_f.should eq(0.091)
jam_track.duration = 1
jam_track.save!
jam_track.reproduction_royalty_amount.to_f.should eq(0.091)
jam_track.duration = 5 * 60 - 1 # just under 5 minutes
jam_track.save!
jam_track.reproduction_royalty_amount.to_f.should eq(0.091)
jam_track.duration = 5 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.to_f.should eq(0.091)
jam_track.duration = 6 * 60 - 1 # just under 6 minutes
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175)
jam_track.duration = 6 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175)
jam_track.duration = 7 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 2)
jam_track.duration = 7 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 2)
jam_track.duration = 8 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 3)
jam_track.duration = 8 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 3)
jam_track.duration = 9 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 4)
jam_track.duration = 9 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 4)
jam_track.duration = 10 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 5)
jam_track.duration = 10 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 5)
jam_track.duration = 11 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 6)
jam_track.duration = 11 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 6)
jam_track.duration = 12 * 60 - 1
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 7)
jam_track.duration = 12 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 7)
jam_track.duration = 13 * 60
jam_track.save!
jam_track.reproduction_royalty_amount.should eq(0.091 + 0.0175 * 8)
end
end
describe 'plays' do
@ -98,6 +182,26 @@ describe JamTrack do
query[1].should eq(jam_track1)
end
it "queries on genre" do
jam_track1 = FactoryGirl.create(:jam_track_with_tracks, original_artist: 'artist', name: 'a')
jam_track2 = FactoryGirl.create(:jam_track_with_tracks, original_artist: 'artist', name: 'b')
jam_track1.genres = [Genre.find('rock')]
jam_track2.genres = [Genre.find('asian')]
jam_track1.save!
jam_track2.save!
query, pager = JamTrack.index({genre: 'rock'}, user)
query.size.should == 1
query[0].should eq(jam_track1)
query, pager = JamTrack.index({genre: 'asian'}, user)
query.size.should == 1
query[0].should eq(jam_track2)
query, pager = JamTrack.index({genre: 'african'}, user)
query.size.should == 0
end
it "supports showing purchased only" do
jam_track1 = FactoryGirl.create(:jam_track_with_tracks, name: 'a')
@ -170,7 +274,7 @@ describe JamTrack do
end
it "100.1234" do
jam_track = FactoryGirl.build(:jam_track, reproduction_royalty_amount: 100.1234)
jam_track = FactoryGirl.build(:jam_track, reproduction_royalty_amount: 100.12345)
jam_track.valid?.should be_false
jam_track.errors[:reproduction_royalty_amount].should == ['is invalid']
end

View File

@ -7,6 +7,7 @@ describe JamTrackTrack do
it "created" do
jam_track_track = FactoryGirl.create(:jam_track_track)
jam_track_track.jam_track.should_not be_nil
jam_track_track.jam_track.reload
jam_track_track.jam_track.jam_track_tracks.should == [jam_track_track]
end

View File

@ -17,6 +17,8 @@ describe 'Musician Search Model' do
describe "creates search obj" do
before(:all) do
User.delete_all
Score.connection.execute('delete from current_network_scores').check
Score.connection.execute('delete from scores').check
end
it "associates to user" do
@ -132,12 +134,13 @@ describe 'Musician Search Model' do
describe "filters interests" do
before(:all) do
MusicianSearch::INTEREST_VALS[1..-1].each do |val|
user_types.each { |utype| FactoryGirl.create(utype, val => true) }
end
end
it "get expected number per interest" do
MusicianSearch::INTEREST_VALS[1..-1].each do |val|
user_types.each { |utype| FactoryGirl.create(utype, val => true, :paid_sessions_hourly_rate=>2300, :paid_sessions_daily_rate=>15000) }
end
search.update_json_value(MusicianSearch::KEY_INTERESTS, MusicianSearch::INTEREST_VALS[1])
expect(search.do_search.count).to eq(user_types.count)
end
@ -169,8 +172,8 @@ describe 'Musician Search Model' do
end
it "gets expected number of users" do
instjson = [{ instrument_id: Instrument.first.id, proficiency_level: 2 },
{ instrument_id: Instrument.first(2)[1].id, proficiency_level: 2 }
instjson = [{ id: Instrument.first.id, level: 2 },
{ id: Instrument.first(2)[1].id, level: 2 }
]
search.update_json_value(MusicianSearch::KEY_INSTRUMENTS, instjson)
expect(search.do_search.count).to eq(3)
@ -201,6 +204,7 @@ describe 'Musician Search Model' do
describe "sort order by latency" do
before(:each) do
User.delete_all
Score.delete_all
t = Time.now - 10.minute
@user1 = FactoryGirl.create(:user, created_at: t+1.minute, last_jam_locidispid: 1)
@ -232,12 +236,12 @@ describe 'Musician Search Model' do
Score.createx(2, 'b', 2, 4, 'd', 4, 70)
end
it "sorts by latency" do
it "sorts by latency", intermittent: true do
search.update_json_value(MusicianSearch::KEY_SORT_ORDER, MusicianSearch::SORT_VALS[0])
results = search.do_search
expect(results[0].id).to eq(@user1.id)
expect(results[0].id).to eq(@user1.id) # HAS FAILED HERE TOO
expect(results[1].id).to eq(@user2.id)
expect(results[2].id).to eq(@user3.id)
expect(results[2].id).to eq(@user3.id) # HAS FAILED INTERMITTENTLY
expect(results[3].id).to eq(@user4.id)
end
@ -286,28 +290,28 @@ describe 'Musician Search Model' do
selections.each do |hash|
search.update_json_value(hash[:key], hash[:value])
json_val = search.json_value(hash[:key])
expect(search.description).to match(/; #{hash[:description]} = #{hash[:lookup][json_val]}/)
expect(search.description).to match(/ #{hash[:description]} = #{hash[:lookup][json_val]}/)
end
end
it 'has correct description for genres' do
search.update_json_value(MusicianSearch::KEY_GENRES, [Genre.first.id, Genre.last.id])
expect(search.description).to match(/; Genres = #{Genre.first.description}, #{Genre.last.description}/)
expect(search.description).to match(/ Genres = #{Genre.first.description}, #{Genre.last.description}/)
end
it 'has correct description for ages' do
search.update_json_value(MusicianSearch::KEY_AGES, [MusicianSearch::AGE_COUNTS[0],MusicianSearch::AGE_COUNTS[1]])
expect(search.description).to match(/; Ages = #{MusicianSearch::AGES[MusicianSearch::AGE_COUNTS[0]]}, #{MusicianSearch::AGES[MusicianSearch::AGE_COUNTS[1]]}/)
expect(search.description).to match(/ Ages = #{MusicianSearch::AGES[MusicianSearch::AGE_COUNTS[0]]}, #{MusicianSearch::AGES[MusicianSearch::AGE_COUNTS[1]]}/)
end
it 'has correct description for instruments' do
instrs = Instrument.limit(2).order(:description)
instjson = [{ instrument_id: instrs[0].id, proficiency_level: 2 },
{ instrument_id: instrs[1].id, proficiency_level: 1 }
instjson = [{ id: instrs[0].id, level: 2 },
{ id: instrs[1].id, level: 1 }
]
search.update_json_value(MusicianSearch::KEY_INSTRUMENTS, instjson)
instr_descrip = "#{instrs[0].description} (#{MusicianSearch::INSTRUMENT_PROFICIENCY[2]}), #{instrs[1].description} (#{MusicianSearch::INSTRUMENT_PROFICIENCY[1]})"
expect(search.description).to match(/; Instruments = #{Regexp.escape(instr_descrip)}/)
instr_descrip = "#{instrs[0].description} / #{MusicianSearch::INSTRUMENT_PROFICIENCY[2]}, #{instrs[1].description} / #{MusicianSearch::INSTRUMENT_PROFICIENCY[1]}"
expect(search.description).to match(/ Instruments = #{Regexp.escape(instr_descrip)}/)
end
end

View File

@ -18,6 +18,13 @@ describe Notification do
@session = FactoryGirl.create(:music_session)
@band = FactoryGirl.create(:band)
@slot1 = FactoryGirl.build(:rsvp_slot, :music_session => @session, :instrument => JamRuby::Instrument.find('electric guitar'))
@slot1.save
@slot2 = FactoryGirl.build(:rsvp_slot, :music_session => @session, :instrument => JamRuby::Instrument.find('drums'))
@slot2.save
@friend_request = FactoryGirl.create(:friend_request, user: @sender, friend: @receiver)
end
@ -671,35 +678,14 @@ describe Notification do
end
end
describe "send scheduled session reminder" do
# it "sends email when user is offline and subscribes to emails" do
# session.creator = sender
# session.save!
# calls = count_publish_to_user_calls
# notification = Notification.send_scheduled_session_cancelled(session)
# UserMailer.deliveries.length.should == 1
# calls[:count].should == 1
# end
# it "does not send email when user is offline and opts out of emails" do
# session.creator = sender
# session.save!
# receiver.subscribe_email = false
# receiver.save!
# calls = count_publish_to_user_calls
# notification = Notification.send_scheduled_session_cancelled(session)
# UserMailer.deliveries.length.should == 0
# calls[:count].should == 1
# end
describe "reminders" do
let(:mail) { UserMailer.deliveries[0] }
before :each do
UserMailer.deliveries.clear
end
it "sends no notification if session is nil" do
calls = count_publish_to_user_calls
notification = Notification.send_scheduled_session_reminder(nil)
notification = Notification.send_session_reminders()
UserMailer.deliveries.length.should == 0
calls[:count].should == 0
@ -707,13 +693,66 @@ describe Notification do
it "sends no notification if there are no rsvp requests" do
calls = count_publish_to_user_calls
notification = Notification.send_scheduled_session_reminder(@session)
notification = Notification.send_session_reminders()
UserMailer.deliveries.length.should == 0
calls[:count].should == 0
end
it "sends email 24 hours before" do
@session.creator = @sender
@session.scheduled_start = Time.now + 23.hours
@session.save!
notification = Notification.send_session_reminders()
UserMailer.deliveries.length.should == 1
calls = count_publish_to_user_calls
calls[:count].should == 0
mail.html_part.body.include?("is scheduled for tomorrow").should be_true
mail.text_part.body.include?("is scheduled for tomorrow").should be_true
mail.html_part.body.include?("starts in 1 hour").should be_false
mail.text_part.body.include?("starts in 1 hour").should be_false
end
it "sends email 1 hour before" do
@session.creator = @sender
@session.scheduled_start = Time.now + 59.minutes
@session.save!
notification = Notification.send_session_reminders()
UserMailer.deliveries.length.should == 1
calls = count_publish_to_user_calls
calls[:count].should == 0
mail.html_part.body.include?("is scheduled for tomorrow").should be_false
mail.text_part.body.include?("is scheduled for tomorrow").should be_false
mail.html_part.body.include?("starts in 1 hour").should be_true
mail.text_part.body.include?("starts in 1 hour").should be_true
end
it "sends notice 5 minutes before" do
UserMailer.deliveries.length.should == 0
receiver_connection = FactoryGirl.create(:connection, user: @receiver)
@receiver.reload
rsvp = RsvpRequest.create({:session_id => @session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "We be jammin!"}, @receiver)
UserMailer.deliveries.clear
calls = count_publish_to_user_calls
@session.creator = @sender
@session.scheduled_start = Time.now + 4.minutes
@session.save!
notification = Notification.send_session_reminders()
calls[:count].should == 1
UserMailer.deliveries.length.should == 0
end
end # reminders
describe "send scheduled session comment" do
# it "sends email when user is offline and subscribes to emails" do
# session.creator = sender

View File

@ -162,6 +162,10 @@ describe OnlinePresence do
let(:player1) { FactoryGirl.create(:user) }
let(:player2) { FactoryGirl.create(:user) }
end
after(:all) {
Band.delete_all
}
end
describe "with a band" do
@ -169,5 +173,10 @@ describe OnlinePresence do
let(:player1) { FactoryGirl.create(:band) }
let(:player2) { FactoryGirl.create(:band) }
end
after(:all) {
Band.delete_all
}
end
end

View File

@ -125,6 +125,15 @@ describe PerformanceSample do
let(:player1) { FactoryGirl.create(:user) }
let(:player2) { FactoryGirl.create(:user) }
end
after(:all) {
User.delete_all
RecordedTrack.delete_all
ClaimedRecording.delete_all
Recording.delete_all
ActiveMusicSession.delete_all
MusicSession.delete_all
}
end
describe "with a band" do
@ -132,5 +141,14 @@ describe PerformanceSample do
let(:player1) { FactoryGirl.create(:band) }
let(:player2) { FactoryGirl.create(:band) }
end
after(:all) {
Band.delete_all
RecordedTrack.delete_all
ClaimedRecording.delete_all
Recording.delete_all
ActiveMusicSession.delete_all
MusicSession.delete_all
}
end
end

View File

@ -373,12 +373,14 @@ describe RsvpRequest do
comment = SessionInfoComment.find_by_creator_id(@session_invitee)
comment.comment.should == "Let's Jam!"
# cancel
expect {RsvpRequest.cancel({:id => rsvp.id, :session_id => @music_session.id, :cancelled => "all", :message => "Sorry, I'm bailing for all sessions"}, @session_invitee)}.to_not raise_error
calendar_count = Calendar.find(:all).count
# cancel & check that calendar has been added:
expect {RsvpRequest.cancel({:id => rsvp.id, :session_id => @music_session.id, :cancelled => "all", :message => "Sorry, I'm bailing for all sessions"}, @session_invitee)}.to_not raise_error
rsvp = RsvpRequest.find_by_id(rsvp.id)
rsvp.canceled.should == true
rsvp.cancel_all.should == true
(Calendar.find(:all).count - calendar_count).should eq(1)
# verify comment
comment = SessionInfoComment.find_by_creator_id(@session_invitee)

View File

@ -195,7 +195,9 @@ describe Sale do
end
it "for a normally priced jam track" do
it "for a normally priced jam track", intermittent: true do
# intermittent: sometimes recurly won't mark it 'collected' soon enough for the test to pass
user.has_redeemable_jamtrack = false
user.save!
shopping_cart = ShoppingCart.create user, jamtrack, 1, false
@ -236,6 +238,7 @@ describe Sale do
sale_line_item.recurly_adjustment_credit_uuid.should be_nil
sale_line_item.recurly_adjustment_uuid.should eq(user.jam_track_rights.last.recurly_adjustment_uuid)
# verify subscription is in Recurly
recurly_account = client.get_account(user)
adjustments = recurly_account.adjustments

View File

@ -3,6 +3,8 @@ require 'spec_helper'
describe "ActiveMusicSessionCleaner" do
before(:all) do
MusicSession.delete_all
ActiveMusicSession.delete_all
@cleaner = ActiveMusicSessionCleaner.new
@cleaner.interval = "INTERVAL '1 second'"
end

View File

@ -0,0 +1,82 @@
require 'spec_helper'
describe 'MusicSessionReminder' do
let(:mail) { UserMailer.deliveries[0] }
before :each do
UserMailer.deliveries.clear
MusicSession.delete_all
User.delete_all
@receiver = FactoryGirl.create(:user)
@sender = FactoryGirl.create(:user)
@session = FactoryGirl.create(:music_session)
@slot1 = FactoryGirl.build(:rsvp_slot, :music_session => @session, :instrument => JamRuby::Instrument.find('electric guitar'))
@slot1.save
@slot2 = FactoryGirl.build(:rsvp_slot, :music_session => @session, :instrument => JamRuby::Instrument.find('drums'))
@slot2.save
end
it "sends email 24 hours before" do
@session.creator = @sender
@session.scheduled_start = Time.now + 23.hours
@session.save!
JamRuby::MusicSessionReminder.perform
UserMailer.deliveries.length.should == 1
calls = count_publish_to_user_calls
calls[:count].should == 0
mail.html_part.body.include?("is scheduled for tomorrow").should be_true
mail.text_part.body.include?("is scheduled for tomorrow").should be_true
mail.html_part.body.include?("starts in 1 hour").should be_false
mail.text_part.body.include?("starts in 1 hour").should be_false
end
it "sends email 1 hour before" do
@session.creator = @sender
@session.scheduled_start = Time.now + 59.minutes
@session.save!
JamRuby::MusicSessionReminder.perform
UserMailer.deliveries.length.should == 1
calls = count_publish_to_user_calls
calls[:count].should == 0
mail.html_part.body.include?("is scheduled for tomorrow").should be_false
mail.text_part.body.include?("is scheduled for tomorrow").should be_false
mail.html_part.body.include?("starts in 1 hour").should be_true
mail.text_part.body.include?("starts in 1 hour").should be_true
end
it "sends notice 5 minutes before" do
UserMailer.deliveries.length.should == 0
receiver_connection = FactoryGirl.create(:connection, user: @receiver)
@receiver.reload
rsvp = RsvpRequest.create({:session_id => @session.id, :rsvp_slots => [@slot1.id, @slot2.id], :message => "We be jammin!"}, @receiver)
UserMailer.deliveries.clear
calls = count_publish_to_user_calls
@session.creator = @sender
@session.scheduled_start = Time.now + 4.minutes
@session.save!
JamRuby::MusicSessionReminder.perform
calls[:count].should == 1
UserMailer.deliveries.length.should == 0
end
def count_publish_to_user_calls
result = {count: 0}
MQRouter.any_instance.stub(:publish_to_user) do |receiver_id, msg|
result[:count] += 1
result[:msg] = msg
end
result
end
end #spec

View File

@ -3,6 +3,7 @@ require 'spec_helper'
describe "MusicSessionScheduler" do
before(:all) do
MusicSession.delete_all
@scheduler = MusicSessionScheduler.new
end

View File

@ -0,0 +1,43 @@
require 'spec_helper'
describe 'DailyJob' do
describe "calendar cleanup" do
shared_examples_for :calendar_cleanup do |trigger_delete, end_count|
before :each do
Calendar.destroy_all
@creator = FactoryGirl.create(:user)
@creator.calendars << Calendar.new(
:name=>"Test Cal",
:description=>"This is a test",
:start_at=>(Time.now),
:end_at=>Time.now,
:trigger_delete=>trigger_delete,
:target_uid=>"2112"
)
end
it "properly purges old 'delete' calendars" do
@creator.reload
@creator.calendars.should have(1).items
JamRuby::DailyJob.perform
@creator.reload
@creator.calendars.should have(1).items
Timecop.travel(Time.now + 5.weeks)
JamRuby::DailyJob.perform
@creator.reload
@creator.calendars.should have(end_count).items
Timecop.return
end
end
describe "whacks old 'delete' calendars" do
it_behaves_like :calendar_cleanup, true, 0
end
describe "doesn't whacks non 'delete' calendars" do
it_behaves_like :calendar_cleanup, false, 1
end
end # calendar cleanpu
end #spec

View File

@ -2,6 +2,10 @@ require 'spec_helper'
describe 'ScheduledMusicSessionCleaner' do
before(:all) {
MusicSession.delete_all
}
it "purges old music sessions" do
[MusicSession::UNSTARTED_INTERVAL_DAYS_PURGE,
MusicSession::UNSTARTED_INTERVAL_DAYS_PURGE_RECUR].each do |interval|

View File

@ -137,6 +137,48 @@ describe UserMailer do
it { mail.text_part.body.include?("to confirm your change in email").should be_true }
end
describe "notifications" do
let(:mail) { UserMailer.deliveries[0] }
let(:music_session) { FactoryGirl.create(:music_session) }
it "should send upcoming email" do
user.update_email = "my_new_email@jamkazam.com"
UserMailer.scheduled_session_reminder_upcoming(music_session.creator, music_session).deliver
UserMailer.deliveries.length.should == 1
mail['from'].to_s.should == UserMailer::DEFAULT_SENDER
mail['to'].to_s.should == music_session.creator.email# rsvp_requests.first.user.email
mail.multipart?.should == true # because we send plain + htm
# verify that the messages are correctly configured
mail.html_part.body.include?("This is a reminder that your JamKazam session").should be_true
mail.text_part.body.include?("This is a reminder that your JamKazam session").should be_true
mail.html_part.body.include?("starts in 1 hour").should be_true
mail.text_part.body.include?("starts in 1 hour").should be_true
end
it "should send 1-day reminder" do
user.update_email = "my_new_email@jamkazam.com"
UserMailer.scheduled_session_reminder_day(music_session.creator, music_session).deliver
UserMailer.deliveries.length.should == 1
mail['from'].to_s.should == UserMailer::DEFAULT_SENDER
mail['to'].to_s.should == music_session.creator.email# rsvp_requests.first.user.email
mail.multipart?.should == true # because we send plain + htm
# verify that the messages are correctly configured
mail.html_part.body.include?("This is a reminder that your JamKazam session").should be_true
mail.text_part.body.include?("This is a reminder that your JamKazam session").should be_true
mail.html_part.body.include?("is scheduled for tomorrow").should be_true
mail.text_part.body.include?("is scheduled for tomorrow").should be_true
end
end
# describe "sends new musicians email" do

View File

@ -14,7 +14,7 @@ require 'resque_failed_job_mailer'
# to prevent embedded resque code from forking
ENV['FORK_PER_JOB'] = 'false'
IS_BUILD_SERVER = !ENV['BUILD_SERVER'].nil?
# recreate test database and migrate it
SpecDb::recreate_database
@ -85,9 +85,13 @@ end
config.run_all_when_everything_filtered = true
config.filter_run :focus
#config.formatter = :documentation
# you can mark a test as slow so that developers won't commonly hit it, but build server will http://blog.davidchelimsky.net/2010/06/14/filtering-examples-in-rspec-2/
config.filter_run_excluding slow: true unless run_tests? :slow
config.filter_run_excluding aws: true unless run_tests? :aws
config.filter_run_excluding intermittent: true if IS_BUILD_SERVER
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
@ -108,7 +112,6 @@ end
config.before(:each) do
stub_const("APP_CONFIG", app_config)
end
config.after(:each) do

View File

@ -206,6 +206,10 @@ def app_config
1
end
def google_public_server_key
"AIzaSyCPTPq5PEcl4XWcm7NZ2IGClZlbsiE8JNo"
end
private
def audiomixer_workspace_path

View File

@ -18,10 +18,11 @@ else
end
end
#gem 'license_finder'
gem 'oj', '2.10.2'
gem 'builder'
gem 'rails', '~>3.2.11'
gem 'railties', '~>3.2.11'
gem 'rails', '~>3.2.22'
gem 'railties', '~>3.2.22'
gem 'jquery-rails'
gem 'jquery-ui-rails', '4.2.1'
gem 'bootstrap-sass', '2.0.4'
@ -36,7 +37,7 @@ gem 'pg', '0.17.1'
gem 'compass-rails', '1.1.3' # 1.1.4 throws an exception on startup about !initialize on nil
gem 'rabl', '0.11.0' # for JSON API development
gem 'gon', '~>4.1.0' # for passthrough of Ruby variables to Javascript variables
gem 'eventmachine', '1.0.3'
gem 'eventmachine', '1.0.4'
gem 'faraday', '~>0.9.0'
gem 'amqp', '0.9.8'
gem 'logging-rails', :require => 'logging/rails'
@ -85,7 +86,7 @@ gem 'slim'
gem 'htmlentities'
gem 'sanitize'
gem 'recurly'
gem 'guard', '2.7.3'
#gem 'guard', '2.7.3'
gem 'influxdb', '0.1.8'
gem 'influxdb-rails', '0.1.10'
gem 'sitemap_generator'
@ -95,18 +96,23 @@ gem 'react-rails', '~> 1.0'
source 'https://rails-assets.org' do
gem 'rails-assets-reflux'
gem 'rails-assets-classnames'
end
#group :development, :production do
# gem 'rack-timeout'
#end
group :development, :test do
gem 'rspec-rails', '2.14.2'
gem "activerecord-import", "~> 0.4.1"
gem 'guard-rspec', '0.5.5'
# gem 'guard-rspec', '0.5.5'
# gem 'jasmine', '1.3.1'
gem 'pry'
gem 'execjs', '1.4.0'
gem 'factory_girl_rails', '4.1.0' # in dev because in use by rake task
gem 'database_cleaner', '1.3.0' #in dev because in use by rake task
gem 'test-unit'
# gem 'teaspoon'
# gem 'teaspoon-jasmine'
gem 'puma'
@ -136,8 +142,8 @@ group :test, :cucumber do
#end
gem 'capybara-screenshot', '0.3.22' # 1.0.0 broke compat with rspec. maybe we need newer rspec
gem 'selenium-webdriver'
gem 'cucumber-rails', :require => false #, '1.3.0', :require => false
gem 'guard-spork', '0.3.2'
# gem 'cucumber-rails', :require => false #, '1.3.0', :require => false
# gem 'guard-spork', '0.3.2'
gem 'spork', '0.9.0'
gem 'launchy', '2.1.1'
gem 'rack-test'

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1021 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

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