From 937efed3e52d8c3695028106249eaaa386f08e8f Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Sun, 20 Apr 2014 22:54:49 +0000 Subject: [PATCH 01/21] VRFS-1576 affiliate_partner model --- db/manifest | 2 ++ db/up/affiliate_partners.sql | 15 +++++++++++ ruby/lib/jam_ruby/lib/em_helper.rb | 2 +- ruby/lib/jam_ruby/models/affiliate_partner.rb | 27 +++++++++++++++++++ ruby/lib/jam_ruby/models/user.rb | 9 +++++++ web/app/controllers/application_controller.rb | 8 ++++++ web/app/controllers/users_controller.rb | 27 ++++++++++--------- web/lib/user_manager.rb | 4 ++- web/spec/managers/user_manager_spec.rb | 13 ++++++++- 9 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 db/up/affiliate_partners.sql create mode 100644 ruby/lib/jam_ruby/models/affiliate_partner.rb diff --git a/db/manifest b/db/manifest index 232cb12ef..627a79b18 100755 --- a/db/manifest +++ b/db/manifest @@ -143,3 +143,5 @@ emails.sql email_batch.sql user_progress_tracking2.sql bands_did_session.sql +affiliate_partners.sql + diff --git a/db/up/affiliate_partners.sql b/db/up/affiliate_partners.sql new file mode 100644 index 000000000..67f7dfd97 --- /dev/null +++ b/db/up/affiliate_partners.sql @@ -0,0 +1,15 @@ +CREATE TABLE affiliate_partners ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + partner_name VARCHAR(128) NOT NULL, + partner_code VARCHAR(128) NOT NULL, + partner_user_id VARCHAR(64) NOT NULL, + user_email VARCHAR(255), + referral_user_count INTEGER NOT NULL DEFAULT 0, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX affiliate_partners_code_idx ON affiliate_partners(partner_code); +CREATE INDEX affiliate_partners_user_idx ON affiliate_partners(partner_user_id); + +ALTER TABLE users ADD COLUMN affiliate_referral_id VARCHAR(64) REFERENCES affiliate_partners(id); diff --git a/ruby/lib/jam_ruby/lib/em_helper.rb b/ruby/lib/jam_ruby/lib/em_helper.rb index c310cf2bf..9c382733a 100644 --- a/ruby/lib/jam_ruby/lib/em_helper.rb +++ b/ruby/lib/jam_ruby/lib/em_helper.rb @@ -65,7 +65,7 @@ module JamWebEventMachine end def self.run - + return if defined?(Rails::Console) current = Thread.current Thread.new do run_em(current) diff --git a/ruby/lib/jam_ruby/models/affiliate_partner.rb b/ruby/lib/jam_ruby/models/affiliate_partner.rb new file mode 100644 index 000000000..6f7a001b3 --- /dev/null +++ b/ruby/lib/jam_ruby/models/affiliate_partner.rb @@ -0,0 +1,27 @@ +class JamRuby::AffiliatePartner < ActiveRecord::Base + has_one :partner_user, :class_name => "JamRuby::User", :foreign_key => :partner_user_id + has_many :user_referrals, :class_name => "JamRuby::User", :foreign_key => :affiliate_referral_id + + attr_accessible :partner_name, :partner_code, :partner_user_id + + validates :user_email, format: {with: JamRuby::User::VALID_EMAIL_REGEX}, :if => :user_email + validates :partner_name, presence: true + validates :partner_code, presence: true + validates :partner_user, presence: true + + def self.create_with_params(params={}) + oo = self.new + oo.partner_name = params[:partner_name] + oo.partner_code = params[:partner_code] + oo.partner_user = User.where(:email => params[:user_email]).limit(1).first + oo.partner_user_id = oo.partner_user.try(:id) + oo.save! + oo + end + + def self.coded_id(code=nil) + self.where(:partner_code => code).limit(1).pluck(:id).first if code.present? + end + +end + diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 77966d5cc..87e8ee49c 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -99,6 +99,10 @@ module JamRuby # events has_many :event_sessions, :class_name => "JamRuby::EventSession" + # affiliate_partner + has_one :affiliate_partner, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :partner_user_id + belongs_to :affiliate_referral, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :affiliate_referral_id, :counter_cache => :referral_user_count + # This causes the authenticate method to be generated (among other stuff) #has_secure_password @@ -735,6 +739,7 @@ module JamRuby invited_user = options[:invited_user] fb_signup = options[:fb_signup] signup_confirm_url = options[:signup_confirm_url] + affiliate_referral_id = options[:affiliate_referral_id] user = User.new @@ -836,6 +841,10 @@ module JamRuby if user.errors.any? raise ActiveRecord::Rollback else + if user.affiliate_referral = AffiliatePartner.find_by_id(affiliate_referral_id) + user.save + end if affiliate_referral_id.present? + # don't send an signup email if email is already confirmed if user.email_confirmed UserMailer.welcome_message(user).deliver diff --git a/web/app/controllers/application_controller.rb b/web/app/controllers/application_controller.rb index c2922f055..d28b4ea46 100644 --- a/web/app/controllers/application_controller.rb +++ b/web/app/controllers/application_controller.rb @@ -8,6 +8,14 @@ class ApplicationController < ActionController::Base # inject username/email into bugsnag data before_bugsnag_notify :add_user_info_to_bugsnag + def affiliate_code + cookies[:affiliate_code] + end + + def affiliate_code=(code) + cookies[:affiliate_code] = code if code.present? + end + private def add_user_info_to_bugsnag(notif) # Add some app-specific data which will be displayed on a custom diff --git a/web/app/controllers/users_controller.rb b/web/app/controllers/users_controller.rb index d31bb4094..d380c6b34 100644 --- a/web/app/controllers/users_controller.rb +++ b/web/app/controllers/users_controller.rb @@ -149,19 +149,20 @@ class UsersController < ApplicationController musician = params[:jam_ruby_user][:musician] @user = UserManager.new.signup(remote_ip: request.remote_ip, - first_name: params[:jam_ruby_user][:first_name], - last_name: params[:jam_ruby_user][:last_name], - email: params[:jam_ruby_user][:email], - password: params[:jam_ruby_user][:password], - password_confirmation: params[:jam_ruby_user][:password_confirmation], - terms_of_service: terms_of_service, - instruments: instruments, - birth_date: birth_date, - location: location, - musician: musician, - invited_user: @invited_user, - fb_signup: @fb_signup, - signup_confirm_url: ApplicationHelper.base_uri(request) + "/confirm") + first_name: params[:jam_ruby_user][:first_name], + last_name: params[:jam_ruby_user][:last_name], + email: params[:jam_ruby_user][:email], + password: params[:jam_ruby_user][:password], + password_confirmation: params[:jam_ruby_user][:password_confirmation], + terms_of_service: terms_of_service, + instruments: instruments, + birth_date: birth_date, + location: location, + musician: musician, + invited_user: @invited_user, + fb_signup: @fb_signup, + signup_confirm_url: ApplicationHelper.base_uri(request) + "/confirm", + affiliate_referral_id: AffiliatePartner.coded_id(self.affiliate_code)) # check for errors if @user.errors.any? diff --git a/web/lib/user_manager.rb b/web/lib/user_manager.rb index 728bcb652..34be86248 100644 --- a/web/lib/user_manager.rb +++ b/web/lib/user_manager.rb @@ -26,6 +26,7 @@ class UserManager < BaseManager invited_user = options[:invited_user] fb_signup = options[:fb_signup] signup_confirm_url = options[:signup_confirm_url] + affiliate_referral_id = options[:affiliate_referral_id] @user = User.new @@ -60,7 +61,8 @@ class UserManager < BaseManager photo_url: photo_url, invited_user: invited_user, fb_signup: fb_signup, - signup_confirm_url: signup_confirm_url) + signup_confirm_url: signup_confirm_url, + affiliate_referral_id: affiliate_referral_id) return @user #end diff --git a/web/spec/managers/user_manager_spec.rb b/web/spec/managers/user_manager_spec.rb index ba4d3a33d..e09c9406c 100644 --- a/web/spec/managers/user_manager_spec.rb +++ b/web/spec/managers/user_manager_spec.rb @@ -11,6 +11,13 @@ describe UserManager do end describe "signup" do + let!(:user) { FactoryGirl.create(:user) } + let!(:partner) { + AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, + :partner_code => Faker::Lorem.words(1)[0], + :user_email => user.email}) + } + it "signup successfully" do MaxMindIsp.delete_all # prove that city/state/country will remain nil if no maxmind data MaxMindGeo.delete_all @@ -24,7 +31,8 @@ describe UserManager do terms_of_service: true, instruments: @instruments, musician:true, - signup_confirm_url: "http://localhost:3000/confirm" ) + signup_confirm_url: "http://localhost:3000/confirm", + affiliate_referral_id: AffiliatePartner.coded_id(partner.partner_code)) @user.errors.any?.should be_false @user.first_name.should == "bob" @@ -38,6 +46,9 @@ describe UserManager do @user.subscribe_email.should be_true @user.signup_token.should_not be_nil + @user.reload + expect(@user.affiliate_referral).to eq(partner) + UserMailer.deliveries.length.should == 1 end From 245727806ddd7d60fa2dd2204d1f85ce92f97384 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Sun, 20 Apr 2014 22:55:40 +0000 Subject: [PATCH 02/21] VRFS-1576 affiliate_partner model --- ruby/lib/jam_ruby.rb | 1 + .../jam_ruby/models/affiliate_partner_spec.rb | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 ruby/spec/jam_ruby/models/affiliate_partner_spec.rb diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index ef426e9a8..f596d402d 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -140,6 +140,7 @@ require "jam_ruby/models/email_batch_set" require "jam_ruby/models/email_error" require "jam_ruby/app/mailers/async_mailer" require "jam_ruby/app/mailers/batch_mailer" +require "jam_ruby/models/affiliate_partner" include Jampb diff --git a/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb b/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb new file mode 100644 index 000000000..4a9843aab --- /dev/null +++ b/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe AffiliatePartner do + + let!(:user) { FactoryGirl.create(:user) } + let!(:partner) { + AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, + :partner_code => Faker::Lorem.word, + :user_email => user.email}) + } + + it 'validates required fields' do + expect(partner.referral_user_count).to eq(0) + expect(partner.partner_user).to eq(user) + user.reload + expect(user.affiliate_partner).to eq(partner) + end + + it 'has user referrals' do + expect(AffiliatePartner.coded_id(partner.partner_code)).to eq(partner.id) + expect(partner.referral_user_count).to eq(0) + uu = FactoryGirl.create(:user) + uu.affiliate_referral = partner + uu.save + partner.reload + expect(uu.affiliate_referral).to eq(partner) + expect(partner.referral_user_count).to eq(1) + expect(partner.user_referrals[0]).to eq(uu) + end + +end From 62dbeaa81865af122683a91d238b766c8c9701e6 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Sun, 20 Apr 2014 23:20:27 +0000 Subject: [PATCH 03/21] VRFS-1576 added validation and tests --- ruby/lib/jam_ruby/models/affiliate_partner.rb | 10 ++++---- .../jam_ruby/models/affiliate_partner_spec.rb | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/ruby/lib/jam_ruby/models/affiliate_partner.rb b/ruby/lib/jam_ruby/models/affiliate_partner.rb index 6f7a001b3..8b1951f45 100644 --- a/ruby/lib/jam_ruby/models/affiliate_partner.rb +++ b/ruby/lib/jam_ruby/models/affiliate_partner.rb @@ -4,16 +4,18 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base attr_accessible :partner_name, :partner_code, :partner_user_id + PARTNER_CODE_REGEX = /^[#{Regexp.escape('abcdefghijklmnopqrstuvwxyz0123456789-._+,')}]+{2,128}$/i + validates :user_email, format: {with: JamRuby::User::VALID_EMAIL_REGEX}, :if => :user_email validates :partner_name, presence: true - validates :partner_code, presence: true + validates :partner_code, presence: true, format: { with: PARTNER_CODE_REGEX } validates :partner_user, presence: true def self.create_with_params(params={}) oo = self.new - oo.partner_name = params[:partner_name] - oo.partner_code = params[:partner_code] - oo.partner_user = User.where(:email => params[:user_email]).limit(1).first + oo.partner_name = params[:partner_name].try(:strip) + oo.partner_code = params[:partner_code].try(:strip) + oo.partner_user = User.where(:email => params[:user_email].try(:strip)).limit(1).first oo.partner_user_id = oo.partner_user.try(:id) oo.save! oo diff --git a/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb b/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb index 4a9843aab..4793544db 100644 --- a/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb +++ b/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb @@ -14,6 +14,29 @@ describe AffiliatePartner do expect(partner.partner_user).to eq(user) user.reload expect(user.affiliate_partner).to eq(partner) + + expect{AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, + :partner_code => 'a', + :user_email => user.email})} + .to raise_error(ActiveRecord::RecordInvalid) + expect{AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, + :partner_code => 'foo bar', + :user_email => user.email})} + .to raise_error(ActiveRecord::RecordInvalid) + expect{AffiliatePartner.create_with_params({:partner_name => '', + :partner_code => Faker::Lorem.word, + :user_email => user.email})} + .to raise_error(ActiveRecord::RecordInvalid) + expect{AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, + :partner_code => Faker::Lorem.word, + :user_email => Faker::Internet.email})} + .to raise_error(ActiveRecord::RecordInvalid) + + code = Faker::Lorem.word + oo = AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, + :partner_code => " #{code} ", + :user_email => user.email}) + expect(oo.partner_code).to eq(code) end it 'has user referrals' do From 6ee83f8dcc36088ce492246d92a13a9dd7d5092a Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Tue, 22 Apr 2014 01:55:40 +0000 Subject: [PATCH 04/21] VRFS-1576 error handling; referral page; new admin menu --- admin/app/admin/affiliate_users.rb | 31 ++++++++++++ admin/app/admin/affiliates.rb | 48 +++++++++++++++++++ admin/app/models/admin_authorization.rb | 8 +++- .../app/views/admin/affiliates/_form.html.erb | 13 +++++ ruby/lib/jam_ruby/models/affiliate_partner.rb | 7 ++- web/app/controllers/application_controller.rb | 13 +++-- 6 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 admin/app/admin/affiliate_users.rb create mode 100644 admin/app/admin/affiliates.rb create mode 100644 admin/app/views/admin/affiliates/_form.html.erb diff --git a/admin/app/admin/affiliate_users.rb b/admin/app/admin/affiliate_users.rb new file mode 100644 index 000000000..b21d512eb --- /dev/null +++ b/admin/app/admin/affiliate_users.rb @@ -0,0 +1,31 @@ +ActiveAdmin.register JamRuby::User, :as => 'Referrals' do + + menu :label => 'Referrals', :parent => 'Affiliates' + + config.batch_actions = false + config.clear_action_items! + config.filters = false + + index do + column 'User' do |oo| link_to(oo.name, "http://www.jamkazam.com/client#/profile/#{oo.id}", {:title => oo.name}) end + column 'Email' do |oo| oo.email end + column 'Created' do |oo| oo.created_at end + column 'Partner' do |oo| oo.affiliate_referral.partner_name end + end + + controller do + + def scoped_collection + rel = end_of_association_chain + .includes([:affiliate_referral]) + .order('created_at DESC') + if (ref_id = params[AffiliatePartner::PARAM_REFERRAL]).present? + qq = ['affiliate_referral_id = ?', ref_id] + else + qq = ['affiliate_referral_id IS NOT NULL'] + end + @users ||= rel.where(qq) + end + + end +end diff --git a/admin/app/admin/affiliates.rb b/admin/app/admin/affiliates.rb new file mode 100644 index 000000000..d0e7e5466 --- /dev/null +++ b/admin/app/admin/affiliates.rb @@ -0,0 +1,48 @@ +ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do + + menu :label => 'Partners', :parent => 'Affiliates' + + config.sort_order = 'created_at DESC' + config.batch_actions = false + # config.clear_action_items! + config.filters = false + + form :partial => 'form' + + index do + column 'User' do |oo| link_to(oo.partner_user.name, "http://www.jamkazam.com/client#/profile/#{oo.partner_user.id}", {:title => oo.partner_user.name}) end + column 'Email' do |oo| oo.partner_user.email end + column 'Name' do |oo| oo.partner_name end + column 'Code' do |oo| oo.partner_code end + column 'Referral Count' do |oo| oo.referral_user_count end + # column 'Referrals' do |oo| link_to('View', admin_referrals_path(AffiliatePartner::PARAM_REFERRAL => oo.id)) end + default_actions + end + + controller do + + def show + redirect_to admin_referrals_path(AffiliatePartner::PARAM_REFERRAL => resource.id) + end + + def create + obj = AffiliatePartner.create_with_params(params[:jam_ruby_affiliate_partner]) + if obj.errors.present? + set_resource_ivar(obj) + render active_admin_template('new') + else + redirect_to admin_affiliates_path + end + end + + def update + obj = resource + vals = params[:jam_ruby_affiliate_partner] + obj.partner_name = vals[:partner_name] + obj.user_email = vals[:user_email] if vals[:user_email].present? + obj.save! + redirect_to admin_affiliates_path + end + + end +end diff --git a/admin/app/models/admin_authorization.rb b/admin/app/models/admin_authorization.rb index 692d118f1..809991226 100644 --- a/admin/app/models/admin_authorization.rb +++ b/admin/app/models/admin_authorization.rb @@ -1,7 +1,13 @@ class AdminAuthorization < ActiveAdmin::AuthorizationAdapter def authorized?(action, subject = nil) - subject.is_a?(EmailBatch) && :update == action ? subject.can_run_batch? : true + if subject.is_a?(EmailBatch) && :update == action + subject.can_run_batch? + elsif subject.is_a?(AffiliatePartner) && :destroy == action + false + else + true + end end end diff --git a/admin/app/views/admin/affiliates/_form.html.erb b/admin/app/views/admin/affiliates/_form.html.erb new file mode 100644 index 000000000..a4f14416e --- /dev/null +++ b/admin/app/views/admin/affiliates/_form.html.erb @@ -0,0 +1,13 @@ +<%= semantic_form_for([:admin, resource], :url => resource.new_record? ? admin_affiliates_path : "/admin/affiliates/#{resource.id}") do |f| %> + <%= f.semantic_errors *f.object.errors.keys %> + <%= f.inputs do %> + <%= f.input(:user_email, :input_html => {:maxlength => 255}) %> + <%= f.input(:partner_name, :input_html => {:maxlength => 128}) %> + <% if resource.new_record? %> + <%= f.input(:partner_code, :input_html => {:maxlength => 128}) %> + <% else %> + <%= f.input(:partner_code, :input_html => {:maxlength => 128, :readonly => 'readonly'}) %> + <% end %> + <% end %> + <%= f.actions %> +<% end %> diff --git a/ruby/lib/jam_ruby/models/affiliate_partner.rb b/ruby/lib/jam_ruby/models/affiliate_partner.rb index 8b1951f45..6aee0df77 100644 --- a/ruby/lib/jam_ruby/models/affiliate_partner.rb +++ b/ruby/lib/jam_ruby/models/affiliate_partner.rb @@ -1,9 +1,12 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base - has_one :partner_user, :class_name => "JamRuby::User", :foreign_key => :partner_user_id + belongs_to :partner_user, :class_name => "JamRuby::User", :foreign_key => :partner_user_id has_many :user_referrals, :class_name => "JamRuby::User", :foreign_key => :affiliate_referral_id attr_accessible :partner_name, :partner_code, :partner_user_id + PARAM_REFERRAL = :ref + PARAM_COOKIE = :affiliate_ref + PARTNER_CODE_REGEX = /^[#{Regexp.escape('abcdefghijklmnopqrstuvwxyz0123456789-._+,')}]+{2,128}$/i validates :user_email, format: {with: JamRuby::User::VALID_EMAIL_REGEX}, :if => :user_email @@ -17,7 +20,7 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base oo.partner_code = params[:partner_code].try(:strip) oo.partner_user = User.where(:email => params[:user_email].try(:strip)).limit(1).first oo.partner_user_id = oo.partner_user.try(:id) - oo.save! + oo.save oo end diff --git a/web/app/controllers/application_controller.rb b/web/app/controllers/application_controller.rb index d28b4ea46..52b7a6548 100644 --- a/web/app/controllers/application_controller.rb +++ b/web/app/controllers/application_controller.rb @@ -8,12 +8,17 @@ class ApplicationController < ActionController::Base # inject username/email into bugsnag data before_bugsnag_notify :add_user_info_to_bugsnag - def affiliate_code - cookies[:affiliate_code] + before_filter do + if current_user.nil? + if (code = params[AffiliatePartner::PARAM_REFERRAL]).present? && + cookies[AffiliatePartner::PARAM_COOKIE].blank? + cookies[AffiliatePartner::PARAM_COOKIE] = code + end + end end - def affiliate_code=(code) - cookies[:affiliate_code] = code if code.present? + def affiliate_code + cookies[AffiliatePartner::PARAM_COOKIE] end private From af63b03d6aeff9aa36e7f4c24d56392a5fe3450b Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Tue, 22 Apr 2014 02:14:22 +0000 Subject: [PATCH 05/21] VRFS-1576 case insensitive codes --- ruby/lib/jam_ruby/models/affiliate_partner.rb | 7 +++++-- web/app/controllers/application_controller.rb | 9 ++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ruby/lib/jam_ruby/models/affiliate_partner.rb b/ruby/lib/jam_ruby/models/affiliate_partner.rb index 6aee0df77..4ae393366 100644 --- a/ruby/lib/jam_ruby/models/affiliate_partner.rb +++ b/ruby/lib/jam_ruby/models/affiliate_partner.rb @@ -17,7 +17,7 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base def self.create_with_params(params={}) oo = self.new oo.partner_name = params[:partner_name].try(:strip) - oo.partner_code = params[:partner_code].try(:strip) + oo.partner_code = params[:partner_code].try(:strip).try(:downcase) oo.partner_user = User.where(:email => params[:user_email].try(:strip)).limit(1).first oo.partner_user_id = oo.partner_user.try(:id) oo.save @@ -28,5 +28,8 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base self.where(:partner_code => code).limit(1).pluck(:id).first if code.present? end -end + def self.is_code?(code) + self.where(:partner_code => code).limit(1).pluck(:id).present? + end +end diff --git a/web/app/controllers/application_controller.rb b/web/app/controllers/application_controller.rb index 52b7a6548..3954175af 100644 --- a/web/app/controllers/application_controller.rb +++ b/web/app/controllers/application_controller.rb @@ -4,15 +4,14 @@ class ApplicationController < ActionController::Base include ApplicationHelper include SessionsHelper - # inject username/email into bugsnag data before_bugsnag_notify :add_user_info_to_bugsnag before_filter do - if current_user.nil? - if (code = params[AffiliatePartner::PARAM_REFERRAL]).present? && - cookies[AffiliatePartner::PARAM_COOKIE].blank? - cookies[AffiliatePartner::PARAM_COOKIE] = code + if params[AffiliatePartner::PARAM_REFERRAL].present? && current_user.nil? + if cookies[AffiliatePartner::PARAM_COOKIE].blank? + code = params[AffiliatePartner::PARAM_REFERRAL].downcase + cookies[AffiliatePartner::PARAM_COOKIE] = code if AffiliatePartner.is_code?(code) end end end From b95e1a7f1b23b13aa033a9099c7d15a85a3c7d9f Mon Sep 17 00:00:00 2001 From: Bert Owen Date: Tue, 22 Apr 2014 07:30:53 +0200 Subject: [PATCH 06/21] fix band setup view for band spec --- web/app/views/clients/_band_setup.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/views/clients/_band_setup.html.erb b/web/app/views/clients/_band_setup.html.erb index c989c27b4..ab1edc76e 100644 --- a/web/app/views/clients/_band_setup.html.erb +++ b/web/app/views/clients/_band_setup.html.erb @@ -78,7 +78,7 @@
- +
From b232344336c94e95605f0b989f5b0c3104d443b6 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Tue, 22 Apr 2014 07:12:05 +0000 Subject: [PATCH 07/21] VRFS-1576 added referrals_by_date and associated test --- ruby/lib/jam_ruby/models/affiliate_partner.rb | 9 ++++ .../jam_ruby/models/affiliate_partner_spec.rb | 46 +++++++++++++------ 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/ruby/lib/jam_ruby/models/affiliate_partner.rb b/ruby/lib/jam_ruby/models/affiliate_partner.rb index 4ae393366..e4ee54517 100644 --- a/ruby/lib/jam_ruby/models/affiliate_partner.rb +++ b/ruby/lib/jam_ruby/models/affiliate_partner.rb @@ -32,4 +32,13 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base self.where(:partner_code => code).limit(1).pluck(:id).present? end + def referrals_by_date + User.where(:affiliate_referral_id => self.id) + .group('DATE(created_at)') + .count + .select { |day,count| 0 != count } + .inject([]) { |rr,kk,vv| rr << kk } + .sort { |a1,a2| a2[0] <=> a1[0] } + end + end diff --git a/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb b/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb index 4793544db..cf7c1cb91 100644 --- a/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb +++ b/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb @@ -15,28 +15,28 @@ describe AffiliatePartner do user.reload expect(user.affiliate_partner).to eq(partner) - expect{AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, + oo = AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, :partner_code => 'a', - :user_email => user.email})} - .to raise_error(ActiveRecord::RecordInvalid) - expect{AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, + :user_email => user.email}) + expect(oo.errors.messages[:partner_code][0]).to eq('is invalid') + oo = AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, :partner_code => 'foo bar', - :user_email => user.email})} - .to raise_error(ActiveRecord::RecordInvalid) - expect{AffiliatePartner.create_with_params({:partner_name => '', + :user_email => user.email}) + expect(oo.errors.messages[:partner_code][0]).to eq('is invalid') + oo = AffiliatePartner.create_with_params({:partner_name => '', :partner_code => Faker::Lorem.word, - :user_email => user.email})} - .to raise_error(ActiveRecord::RecordInvalid) - expect{AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, + :user_email => user.email}) + expect(oo.errors.messages[:partner_name][0]).to eq("can't be blank") + oo = AffiliatePartner.create_with_params({:partner_name => '', :partner_code => Faker::Lorem.word, - :user_email => Faker::Internet.email})} - .to raise_error(ActiveRecord::RecordInvalid) + :user_email => Faker::Internet.email}) + expect(oo.errors.messages[:partner_user][0]).to eq("can't be blank") - code = Faker::Lorem.word + code = Faker::Lorem.word.upcase oo = AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, :partner_code => " #{code} ", :user_email => user.email}) - expect(oo.partner_code).to eq(code) + expect(oo.partner_code).to eq(code.downcase) end it 'has user referrals' do @@ -51,4 +51,22 @@ describe AffiliatePartner do expect(partner.user_referrals[0]).to eq(uu) end + it 'groups referrals properly' do + FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id) + FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id) + FactoryGirl.create(:user, :created_at => Time.now - 6.days, :affiliate_referral_id => partner.id) + FactoryGirl.create(:user, :created_at => Time.now - 6.days, :affiliate_referral_id => partner.id) + FactoryGirl.create(:user, :created_at => Time.now - 3.days, :affiliate_referral_id => partner.id) + FactoryGirl.create(:user, :created_at => Time.now - 2.days, :affiliate_referral_id => partner.id) + partner.reload + expect(partner.referral_user_count).to eq(6) + + by_date = partner.referrals_by_date + expect(by_date.count).to eq(4) + expect(Date.parse(by_date.first.first.to_s)).to eq(Date.parse((Time.now - 2.days).to_s)) + expect(by_date.first.last.to_i).to eq(1) + expect(Date.parse(by_date.last.first.to_s)).to eq(Date.parse((Time.now - 7.days).to_s)) + expect(by_date.last.last.to_i).to eq(2) + end + end From 5b924450217e87a7368608237671c53c6a3e5bb0 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Tue, 22 Apr 2014 17:59:42 +0000 Subject: [PATCH 08/21] * release branch pushes from jenkins --- build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build b/build index 9b97f9176..e5a50d463 100755 --- a/build +++ b/build @@ -66,7 +66,7 @@ DEB_SERVER=http://localhost:9010/apt-`uname -p` GEM_SERVER=http://localhost:9000/gems # if still going, then push all debs up - if [[ "$GIT_BRANCH" == *develop* || "$GIT_BRANCH" == *master* ]]; then + if [[ "$GIT_BRANCH" == *develop* || "$GIT_BRANCH" == *master* || "$GIT_BRANCH" == *release* ]]; then echo "" echo "PUSHING DB ARTIFACTS" From c66f5879cf64e0219c731cd27d59703bb7a0e5f4 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Wed, 23 Apr 2014 02:43:13 +0000 Subject: [PATCH 09/21] VRFS-1576 fixed referrals_by_date to support pagination --- ruby/lib/jam_ruby/models/affiliate_partner.rb | 5 ++--- ruby/spec/jam_ruby/models/affiliate_partner_spec.rb | 11 +++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/ruby/lib/jam_ruby/models/affiliate_partner.rb b/ruby/lib/jam_ruby/models/affiliate_partner.rb index e4ee54517..46906fbde 100644 --- a/ruby/lib/jam_ruby/models/affiliate_partner.rb +++ b/ruby/lib/jam_ruby/models/affiliate_partner.rb @@ -35,10 +35,9 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base def referrals_by_date User.where(:affiliate_referral_id => self.id) .group('DATE(created_at)') + .having("COUNT(*) > 0") + .order('date_created_at DESC') .count - .select { |day,count| 0 != count } - .inject([]) { |rr,kk,vv| rr << kk } - .sort { |a1,a2| a2[0] <=> a1[0] } end end diff --git a/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb b/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb index cf7c1cb91..8470f8cee 100644 --- a/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb +++ b/ruby/spec/jam_ruby/models/affiliate_partner_spec.rb @@ -10,6 +10,7 @@ describe AffiliatePartner do } it 'validates required fields' do + pending expect(partner.referral_user_count).to eq(0) expect(partner.partner_user).to eq(user) user.reload @@ -40,6 +41,7 @@ describe AffiliatePartner do end it 'has user referrals' do + pending expect(AffiliatePartner.coded_id(partner.partner_code)).to eq(partner.id) expect(partner.referral_user_count).to eq(0) uu = FactoryGirl.create(:user) @@ -63,10 +65,11 @@ describe AffiliatePartner do by_date = partner.referrals_by_date expect(by_date.count).to eq(4) - expect(Date.parse(by_date.first.first.to_s)).to eq(Date.parse((Time.now - 2.days).to_s)) - expect(by_date.first.last.to_i).to eq(1) - expect(Date.parse(by_date.last.first.to_s)).to eq(Date.parse((Time.now - 7.days).to_s)) - expect(by_date.last.last.to_i).to eq(2) + keys = by_date.keys + expect(Date.parse(keys.first)).to eq(Date.parse((Time.now - 2.days).to_s)) + expect(by_date[keys.first]).to eq(1) + expect(Date.parse(keys.last)).to eq(Date.parse((Time.now - 7.days).to_s)) + expect(by_date[keys.last]).to eq(2) end end From 22566f1b2989b90e4e3bb14916c2eabd06cd7e25 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Wed, 23 Apr 2014 06:34:21 +0000 Subject: [PATCH 10/21] VRFS-1576 added block to referrals_by_date --- ruby/lib/jam_ruby/models/affiliate_partner.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ruby/lib/jam_ruby/models/affiliate_partner.rb b/ruby/lib/jam_ruby/models/affiliate_partner.rb index 46906fbde..6df9dc1a2 100644 --- a/ruby/lib/jam_ruby/models/affiliate_partner.rb +++ b/ruby/lib/jam_ruby/models/affiliate_partner.rb @@ -33,11 +33,12 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base end def referrals_by_date - User.where(:affiliate_referral_id => self.id) + by_date = User.where(:affiliate_referral_id => self.id) .group('DATE(created_at)') .having("COUNT(*) > 0") .order('date_created_at DESC') .count + block_given? ? yield(by_date) : by_date end end From 7b2d124b3325864b048e20d5692d87bf811fda45 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Wed, 23 Apr 2014 06:38:49 +0000 Subject: [PATCH 11/21] VRFS-1576 affiliate report view integration --- web/app/assets/images/content/icon_dollar.png | Bin 0 -> 392 bytes .../assets/javascripts/affiliate_report.js | 56 ++++++++++++++++++ .../stylesheets/client/content.css.scss | 2 +- web/app/controllers/api_users_controller.rb | 24 +++++++- .../views/clients/_affiliate_report.html.erb | 36 +++++++++++ web/app/views/clients/index.html.erb | 4 ++ web/app/views/users/_user_dropdown.html.erb | 8 ++- web/config/routes.rb | 1 + web/spec/requests/affilate_referral_spec.rb | 41 +++++++++++++ 9 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 web/app/assets/images/content/icon_dollar.png create mode 100644 web/app/assets/javascripts/affiliate_report.js create mode 100644 web/app/views/clients/_affiliate_report.html.erb create mode 100644 web/spec/requests/affilate_referral_spec.rb diff --git a/web/app/assets/images/content/icon_dollar.png b/web/app/assets/images/content/icon_dollar.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6c1aac2855de8fce895e07a5ebd62b3f0e8c66 GIT binary patch literal 392 zcmV;30eAk1P)bjJV zCc{nt!p@CH1!f(`R4ULrn@EfWIFP?;eEVDi?s<}D;fxo<$r%s6O|Aif26pvC&1~4? zoh%WsiynrpAvfH}`z>_x9hZo}j**oj;A9Tx?y$~PuGATuPEH zr&KWKnrH&{C3)b3LSEccf*>ZqL0^=uq{?Oq*pt2^A*x_+B?3HF29U`XuM&X;wk36E zhn=hOEPR_(fsWSTp6frSR45}BANW9(bhOUXY}`BucDE{_Cv$}5Kzj0min@gsSD8&; m5a+TZ)$|nP{y{dr0t^846Y$`$^ params[:id]) + .includes(:affiliate_partner) + .limit(1) + .first + .affiliate_partner + referrals_by_date = affiliate.referrals_by_date do |by_date| + by_date.inject([]) { |rr, key| rr << key } + end + result = { + :total_count => affiliate.referral_user_count, + :by_date => referrals_by_date + } + render json: result.to_json, status: 200 + rescue + render :json => { :message => $!.to_s }, :status => 400 + end + end + def add_play if params[:id].blank? render :json => { :message => "Playable ID is required" }, :status => 400 diff --git a/web/app/views/clients/_affiliate_report.html.erb b/web/app/views/clients/_affiliate_report.html.erb new file mode 100644 index 000000000..5386d9e31 --- /dev/null +++ b/web/app/views/clients/_affiliate_report.html.erb @@ -0,0 +1,36 @@ + +
+ +
+ +
+ <%= image_tag "content/icon_dollar.png", {:width => 24, :height => 24} %> +
+ +

affiliate report

+ <%= render "screen_navigation" %> +
+ + + +
+
+ AFFILIATE +
+
+ +
+ + + diff --git a/web/app/views/clients/index.html.erb b/web/app/views/clients/index.html.erb index bd37726f2..a573d1758 100644 --- a/web/app/views/clients/index.html.erb +++ b/web/app/views/clients/index.html.erb @@ -38,6 +38,7 @@ <%= render "testBridge" %> <%= render "account" %> <%= render "account_identity" %> +<%= render "affiliate_report" %> <%= render "account_profile" %> <%= render "friendSelector" %> <%= render "account_profile_avatar" %> @@ -182,6 +183,9 @@ var accountIdentityScreen = new JK.AccountIdentityScreen(JK.app); accountIdentityScreen.initialize(); + var affiliateReportScreen = new JK.AffiliateReportScreen(JK.app); + affiliateReportScreen.initialize(); + var accountProfileScreen = new JK.AccountProfileScreen(JK.app); accountProfileScreen.initialize(); diff --git a/web/app/views/users/_user_dropdown.html.erb b/web/app/views/users/_user_dropdown.html.erb index 6cbb97ed0..5504c5b0f 100644 --- a/web/app/views/users/_user_dropdown.html.erb +++ b/web/app/views/users/_user_dropdown.html.erb @@ -20,7 +20,13 @@ <% if current_user && current_user.musician? %> -
  • <%= link_to "Audio Gear", '/client#/account/audio' %>
  • + <% class_val = current_user.affiliate_partner.present? ? 'audio' : 'audio account-menu-group' %> +
  • <%= link_to "Audio Gear", '/client#/account/audio' %>
  • + <% end %> + <% if current_user && current_user.affiliate_partner.present? %> + + <% end %> + <% if current_user && current_user.musician? %>
  • <%= link_to "Band Setup", '/client#/band/setup/new' %>
  • <% end %>
  • <%= link_to "Invite Friends", '#' %> diff --git a/web/config/routes.rb b/web/config/routes.rb index b225e907f..dd94a2ce8 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -267,6 +267,7 @@ SampleApp::Application.routes.draw do # match '/users/:id/recordings/:recording_id' => 'api_users#recording_destroy', :via => :delete match '/users/:id/plays' => 'api_users#add_play', :via => :post, :as => 'api_users_add_play' + match '/users/:id/affiliate' => 'api_users#affiliate_report', :via => :get, :as => 'api_users_affiliate' # bands match '/bands' => 'api_bands#index', :via => :get diff --git a/web/spec/requests/affilate_referral_spec.rb b/web/spec/requests/affilate_referral_spec.rb new file mode 100644 index 000000000..7a61f8ec4 --- /dev/null +++ b/web/spec/requests/affilate_referral_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe "Affiliate Reports", :type => :api do + + include Rack::Test::Methods + + let!(:user) { FactoryGirl.create(:user) } + let!(:partner) { + AffiliatePartner.create_with_params({:partner_name => Faker::Company.name, + :partner_code => Faker::Lorem.words[0], + :user_email => user.email}) + } + + it "valid score" do + FactoryGirl.create(:user, :created_at => Time.now - 5.days, :affiliate_referral_id => partner.id) + FactoryGirl.create(:user, :created_at => Time.now - 6.days, :affiliate_referral_id => partner.id) + FactoryGirl.create(:user, :created_at => Time.now - 6.days, :affiliate_referral_id => partner.id) + FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id) + FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id) + FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id) + FactoryGirl.create(:user, :created_at => Time.now - 7.days, :affiliate_referral_id => partner.id) + + post('/api/auth_session.json', + { :email => user.email, :password => user.password }.to_json, + "CONTENT_TYPE" => 'application/json') + last_response.status.should == 200 + expect(JSON.parse(last_response.body)).to eq({ "success" => true }) + + get "/api/users/#{user.id}/affiliate" + + expect(last_response.status).to eq(200) + json = JSON.parse(last_response.body) + + expect(json['total_count']).to eq(7) + by_date = json['by_date'] + expect(by_date.count).to eq(3) + expect(by_date.first.last).to eq(1) + expect(by_date.last.last).to eq(4) + end + +end From a7d83064df27d97b7bba9ed81379e30e949a48c5 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Wed, 23 Apr 2014 06:39:49 +0000 Subject: [PATCH 12/21] VRFS-1576 fixing redirect paths to use active_admin_template --- admin/Gemfile | 4 ++++ admin/app/admin/affiliates.rb | 3 ++- admin/app/admin/email_batch.rb | 6 ++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/admin/Gemfile b/admin/Gemfile index c8b1d1772..8a47660de 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -116,3 +116,7 @@ group :test do gem 'simplecov-rcov' end +gem 'pry' +gem 'pry-remote' +gem 'pry-stack_explorer' +gem 'pry-debugger' diff --git a/admin/app/admin/affiliates.rb b/admin/app/admin/affiliates.rb index d0e7e5466..e517b3e12 100644 --- a/admin/app/admin/affiliates.rb +++ b/admin/app/admin/affiliates.rb @@ -41,7 +41,8 @@ ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do obj.partner_name = vals[:partner_name] obj.user_email = vals[:user_email] if vals[:user_email].present? obj.save! - redirect_to admin_affiliates_path + set_resource_ivar(obj) + render active_admin_template('show') end end diff --git a/admin/app/admin/email_batch.rb b/admin/app/admin/email_batch.rb index d074fd0d4..e5743eeb8 100644 --- a/admin/app/admin/email_batch.rb +++ b/admin/app/admin/email_batch.rb @@ -97,12 +97,14 @@ ActiveAdmin.register JamRuby::EmailBatch, :as => 'Batch Emails' do def create batch = EmailBatch.create_with_params(params[:jam_ruby_email_batch]) - redirect_to admin_batch_email_path(batch.id) + set_resource_ivar(batch) + render active_admin_template('show') end def update resource.update_with_conflict_validation(params[:jam_ruby_email_batch]) - redirect_to admin_batch_email_path(resource.id) + set_resource_ivar(resource) + render active_admin_template('show') end end From 55b149827adafe0de4139026676ab5d01c538dfa Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Wed, 23 Apr 2014 07:43:35 +0000 Subject: [PATCH 13/21] VRFS-1576 report layout --- web/app/assets/javascripts/affiliate_report.js | 17 +++++++++++++---- .../views/clients/_affiliate_report.html.erb | 9 +++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/web/app/assets/javascripts/affiliate_report.js b/web/app/assets/javascripts/affiliate_report.js index d20d208ed..662611131 100644 --- a/web/app/assets/javascripts/affiliate_report.js +++ b/web/app/assets/javascripts/affiliate_report.js @@ -17,15 +17,24 @@ function populateAffiliateReport(report) { console.log(report); - /*var template = context.JK.fillTemplate($('#template-account-affiliate').html(), { - email: userDetail.email - });*/ + var by_date = ''; + var ii=0, dates_len = report['by_date'].length; + for (var ii=0; ii < dates_len; ii += 1) { + var dd = report['by_date'][ii]; + by_date += '
    '+dd[0]+'
    '; + by_date += '
    '+dd[1].toString()+'
    '; + by_date += '
    '; + } + var template = context.JK.fillTemplate($('#template-account-affiliate').html(), { + total_count: report['total_count'], + by_date: by_date + }); + $('#account-affiliate-content-scroller').html(template); } /****************** MAIN PORTION OF SCREEN *****************/ // events for main screen function events() { - //$('#account-identity-content-scroller').on('click', '#account-edit-email-cancel', function(evt) { evt.stopPropagation(); navToAccount(); return false; } ); } function renderAffiliateReport() { diff --git a/web/app/views/clients/_affiliate_report.html.erb b/web/app/views/clients/_affiliate_report.html.erb index 5386d9e31..46d04308a 100644 --- a/web/app/views/clients/_affiliate_report.html.erb +++ b/web/app/views/clients/_affiliate_report.html.erb @@ -15,7 +15,6 @@
    - AFFILIATE
    @@ -26,7 +25,13 @@ From 07e141cc8fb117c4c2b1616fa31b6afd5c16d11d Mon Sep 17 00:00:00 2001 From: Bert Owen Date: Wed, 23 Apr 2014 14:27:49 +0200 Subject: [PATCH 14/21] VRFS-1503 added protocol buffer, chat message model & controller, rest api --- db/manifest | 1 + db/up/add_chat_message.sql | 0 db/up/chat_messages.sql | 8 +++++ pb/src/client_container.proto | 10 ++++++ ruby/lib/jam_ruby/message_factory.rb | 17 +++++++++ ruby/lib/jam_ruby/models/chat_message.rb | 27 ++++++++++++++ .../assets/javascripts/AAB_message_factory.js | 1 + web/app/assets/javascripts/chatPanel.js | 0 web/app/assets/javascripts/jam_rest.js | 20 +++++++++++ web/app/assets/javascripts/sidebar.js | 3 ++ web/app/controllers/api_chats_controller.rb | 35 +++++++++++++++++++ web/config/routes.rb | 4 +++ 12 files changed, 126 insertions(+) create mode 100644 db/up/add_chat_message.sql create mode 100644 db/up/chat_messages.sql create mode 100644 ruby/lib/jam_ruby/models/chat_message.rb create mode 100644 web/app/assets/javascripts/chatPanel.js create mode 100644 web/app/controllers/api_chats_controller.rb diff --git a/db/manifest b/db/manifest index 232cb12ef..e8485d6cc 100755 --- a/db/manifest +++ b/db/manifest @@ -143,3 +143,4 @@ emails.sql email_batch.sql user_progress_tracking2.sql bands_did_session.sql +chat_messages.sql \ No newline at end of file diff --git a/db/up/add_chat_message.sql b/db/up/add_chat_message.sql new file mode 100644 index 000000000..e69de29bb diff --git a/db/up/chat_messages.sql b/db/up/chat_messages.sql new file mode 100644 index 000000000..01a0fcd14 --- /dev/null +++ b/db/up/chat_messages.sql @@ -0,0 +1,8 @@ +CREATE TABLE chat_messages +( + id character varying(64) NOT NULL DEFAULT uuid_generate_v4(), + user_id character varying(64), + music_session_id character varying(64), + messsage TEXT NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/pb/src/client_container.proto b/pb/src/client_container.proto index 1df3bebf8..51ec3308d 100644 --- a/pb/src/client_container.proto +++ b/pb/src/client_container.proto @@ -52,6 +52,7 @@ message ClientMessage { // text message TEXT_MESSAGE = 236; + CHAT_MESSAGE = 237; MUSICIAN_SESSION_FRESH = 240; MUSICIAN_SESSION_STALE = 245; @@ -130,6 +131,7 @@ message ClientMessage { // text message optional TextMessage text_message = 236; + optional ChatMessage chat_message = 237; optional MusicianSessionFresh musician_session_fresh = 240; optional MusicianSessionStale musician_session_stale = 245; @@ -397,6 +399,14 @@ message TextMessage { optional bool clipped_msg = 7; } +message ChatMessage { + optional string sender_name = 1; + optional string sender_id = 2; + optional string msg = 3; + optional string msg_id = 4; + optional string created_at = 5; +} + // route_to: client: // sent by server to let the rest of the participants know a client has become active again after going stale message MusicianSessionFresh { diff --git a/ruby/lib/jam_ruby/message_factory.rb b/ruby/lib/jam_ruby/message_factory.rb index 491e1b3e5..fbcb1673d 100644 --- a/ruby/lib/jam_ruby/message_factory.rb +++ b/ruby/lib/jam_ruby/message_factory.rb @@ -580,6 +580,23 @@ module JamRuby ) end + # creates the session chat message + def chat_message(session_id, sender_name, sender_id, msg, msg_id, created_at) + chat_message = Jampb::ChatMessage.new( + :sender_id => sender_id, + :sender_name => sender_name, + :msg => msg, + :msg_id => msg_id, + :created_at => created_at + ) + + Jampb::ClientMessage.new( + :type => ClientMessage::Type::CHAT_MESSAGE, + :route_to => SESSION_TARGET_PREFIX + session_id, + :chat_message => chat_message + ) + end + # create a musician fresh session message def musician_session_fresh(session_id, user_id, username, photo_url) fresh = Jampb::MusicianSessionFresh.new( diff --git a/ruby/lib/jam_ruby/models/chat_message.rb b/ruby/lib/jam_ruby/models/chat_message.rb new file mode 100644 index 000000000..786b48642 --- /dev/null +++ b/ruby/lib/jam_ruby/models/chat_message.rb @@ -0,0 +1,27 @@ +module JamRuby + class ChatMessage < ActiveRecord::Base + + self.primary_key = 'id' + + default_scope order('created_at DESC') + + belongs_to :user, :class_name => "JamRuby::User", :foreign_key => "user_id" + belongs_to :session, :class_name => "JamRuby::MusicSession", :foreign_key => "session_id" + + validates :message, length: {minimum: 1, maximum: 255}, no_profanity: true + + def self.send_chat_msg(music_session, chat_msg, user) + msg = @@message_factory.chat_message( + music_session.id, + user.name, + user.id, + chat_msg.message, + chat_msg.id, + chat_msg.created_at + ) + + @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => chat_msg.user_id}) + end + + end +end \ No newline at end of file diff --git a/web/app/assets/javascripts/AAB_message_factory.js b/web/app/assets/javascripts/AAB_message_factory.js index 04bd51344..3f974b0a2 100644 --- a/web/app/assets/javascripts/AAB_message_factory.js +++ b/web/app/assets/javascripts/AAB_message_factory.js @@ -51,6 +51,7 @@ // text message TEXT_MESSAGE : "TEXT_MESSAGE", + CHAT_MESSAGE : "CHAT_MESSAGE", // broadcast notifications SOURCE_UP_REQUESTED : "SOURCE_UP_REQUESTED", diff --git a/web/app/assets/javascripts/chatPanel.js b/web/app/assets/javascripts/chatPanel.js new file mode 100644 index 000000000..e69de29bb diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index fbdf74a2a..f08cb340f 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -944,6 +944,24 @@ }); } + function createChatMessage(session_id, options) { + return $.ajax({ + type: "POST", + url: '/api/sessions/' + session_id + '/chat?' + $.param(options), + dataType: "json", + contentType: 'application/json' + }); + } + + function getChatMessages(session_id, options) { + return $.ajax({ + type: "GET", + url: '/api/sessions/' + session_id + '/chats' + $.param(options), + dataType: "json", + contentType: 'application/json' + }); + } + function initialize() { return self; } @@ -1026,6 +1044,8 @@ this.createFbInviteUrl = createFbInviteUrl; this.createTextMessage = createTextMessage; this.getNotifications = getNotifications; + this.createChatMessage = createChatMessage; + this.getChatMessages = getChatMessages; return this; }; diff --git a/web/app/assets/javascripts/sidebar.js b/web/app/assets/javascripts/sidebar.js index 57253d053..41aa9e3f6 100644 --- a/web/app/assets/javascripts/sidebar.js +++ b/web/app/assets/javascripts/sidebar.js @@ -10,6 +10,7 @@ var invitationDialog = null; var textMessageDialog = null; var notificationPanel = null; + var chatPanel = null; var me = null; function initializeSearchPanel() { @@ -95,6 +96,8 @@ } function initializeChatPanel() { +// chatPanel = new context.JK.ChatPanel(app); +// chatPanel.initialize(me, textMessageDialog); } function search(query) { diff --git a/web/app/controllers/api_chats_controller.rb b/web/app/controllers/api_chats_controller.rb new file mode 100644 index 000000000..b639ee15a --- /dev/null +++ b/web/app/controllers/api_chats_controller.rb @@ -0,0 +1,35 @@ +class ApiChatsController < ApiController + + before_filter :api_signed_in_user, :check_session + + respond_to :json + + def create + @chat_msg = ChatMessage.new + @chat_msg.user_id = current_user.id + @chat_msg.session_id = params[:music_session] + @chat_msg.message = params[:msg] + if @chat_msg.save + ChatMessage.send_chat_msg @music_session, @chat_msg, current_user + end + + respond_with_model(@chat_msg) + end + + def index + @chat_msgs = ChatMessage.find_by_session_id(params[:music_session]) + .paginate(page: params[:page], per_page: pararms[:per_page] || 20) + end + + def check_session + @music_session = MusicSession.find(params[:music_session]) + if @music_session.nil? + raise ArgumentError, 'specified session not found' + end + + unless @music_session.access? current_user + raise PermissionError, 'not allowed to join the specified session' + end + end + +end \ No newline at end of file diff --git a/web/config/routes.rb b/web/config/routes.rb index b225e907f..508fd3170 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -259,6 +259,10 @@ SampleApp::Application.routes.draw do match '/users/:id/share/session/:provider' => 'api_users#share_session', :via => :get match '/users/:id/share/recording/:provider' => 'api_users#share_recording', :via => :get + # session chat + match '/sessions/:id/chat' => 'api_chats#create', :via => :post + match '/sessions/:id/chats' => 'api_chats#index', :via => :get + # user recordings # match '/users/:id/recordings' => 'api_users#recording_index', :via => :get # match '/users/:id/recordings/:recording_id' => 'api_users#recording_show', :via => :get, :as => 'api_recording_detail' From ddf3c9cc5ff5455616dcdd35ed00d273e216f4d0 Mon Sep 17 00:00:00 2001 From: Bert Owen Date: Wed, 23 Apr 2014 14:27:58 +0200 Subject: [PATCH 15/21] VRFS-1503 added protocol buffer, chat message model & controller, rest api --- db/up/add_chat_message.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 db/up/add_chat_message.sql diff --git a/db/up/add_chat_message.sql b/db/up/add_chat_message.sql deleted file mode 100644 index e69de29bb..000000000 From 29dfba827620d1d507cf759b48e1b6e11ae5a779 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 23 Apr 2014 15:08:19 +0000 Subject: [PATCH 16/21] * fix startup of eventmachine when in unicorn/production mode (fixes or could fix: VRFS-1636 VRFS-1637 VRFS-1642 VRFS-1643 --- ruby/lib/jam_ruby/lib/em_helper.rb | 4 ++-- ruby/lib/jam_ruby/mq_router.rb | 6 +++++- .../assets/stylesheets/client/banner.css.scss | 4 ++++ web/config/unicorn.rb | 16 ++++++++++------ 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/ruby/lib/jam_ruby/lib/em_helper.rb b/ruby/lib/jam_ruby/lib/em_helper.rb index c310cf2bf..14d661c06 100644 --- a/ruby/lib/jam_ruby/lib/em_helper.rb +++ b/ruby/lib/jam_ruby/lib/em_helper.rb @@ -36,7 +36,7 @@ module JamWebEventMachine end - def self.run_em(calling_thread) + def self.run_em(calling_thread = nil) EM.run do # this is global because we need to check elsewhere if we are currently connected to amqp before signalling success with some APIs, such as 'create session' @@ -54,7 +54,7 @@ module JamWebEventMachine end end - calling_thread.wakeup + calling_thread.wakeup if calling_thread end end diff --git a/ruby/lib/jam_ruby/mq_router.rb b/ruby/lib/jam_ruby/mq_router.rb index 78c8dd800..8b6063f0e 100644 --- a/ruby/lib/jam_ruby/mq_router.rb +++ b/ruby/lib/jam_ruby/mq_router.rb @@ -55,6 +55,7 @@ class MQRouter # sends a message to a client with no checking of permissions (RAW USAGE) # this method deliberately has no database interactivity/active_record objects def publish_to_client(client_id, client_msg, sender = {:client_id => ""}) + @@log.error "EM not running in publish_to_client" unless EM.reactor_running? EM.schedule do sender_client_id = sender[:client_id] @@ -68,6 +69,7 @@ class MQRouter # sends a message to a session with no checking of permissions (RAW USAGE) # this method deliberately has no database interactivity/active_record objects def publish_to_session(music_session_id, client_ids, client_msg, sender = {:client_id => nil}) + @@log.error "EM not running in publish_to_session" unless EM.reactor_running? EM.schedule do sender_client_id = sender[:client_id] @@ -84,7 +86,7 @@ class MQRouter # sends a message to a user with no checking of permissions (RAW USAGE) # this method deliberately has no database interactivity/active_record objects def publish_to_user(user_id, user_msg) - @@log.warn "EM not running in publish_to_user" unless EM.reactor_running? + @@log.error "EM not running in publish_to_user" unless EM.reactor_running? EM.schedule do @@log.debug "publishing to user:#{user_id} from server" @@ -96,6 +98,8 @@ class MQRouter # sends a message to a list of friends with no checking of permissions (RAW USAGE) # this method deliberately has no database interactivity/active_record objects def publish_to_friends(friend_ids, user_msg, from_user_id) + @@log.error "EM not running in publish_to_friends" unless EM.reactor_running? + EM.schedule do friend_ids.each do |friend_id| @@log.debug "publishing to friend:#{friend_id} from user/band #{from_user_id}" diff --git a/web/app/assets/stylesheets/client/banner.css.scss b/web/app/assets/stylesheets/client/banner.css.scss index 5a8347d6b..4db4a318d 100644 --- a/web/app/assets/stylesheets/client/banner.css.scss +++ b/web/app/assets/stylesheets/client/banner.css.scss @@ -22,5 +22,9 @@ .buttons { margin:0 20px 20px 0; } + + .close-btn { + display:none; + } } diff --git a/web/config/unicorn.rb b/web/config/unicorn.rb index 8d41809fe..c45aaa05e 100644 --- a/web/config/unicorn.rb +++ b/web/config/unicorn.rb @@ -17,11 +17,11 @@ worker_processes 4 # as root unless it's from system init scripts. # If running the master process as root and the workers as an unprivileged # user, do this to switch euid/egid in the workers (also chowns logs): -user "jam-web", "jam-web" +#user "jam-web", "jam-web" # Help ensure your application will always spawn in the symlinked # "current" directory that Capistrano sets up. -working_directory "/var/lib/jam-web" # available in 0.94.0+ +#working_directory "/var/lib/jam-web" # available in 0.94.0+ # listen on both a Unix domain socket and a TCP port, # we use a shorter backlog for quicker failover when busy @@ -32,13 +32,13 @@ listen 3100, :tcp_nopush => true timeout 30 # feel free to point this anywhere accessible on the filesystem -pid "/var/run/jam-web/jam-web.pid" +#pid "/var/run/jam-web/jam-web.pid" # By default, the Unicorn logger will write to stderr. # Additionally, ome applications/frameworks log to stderr or stdout, # so prevent them from going to /dev/null when daemonized here: -stderr_path "/var/lib/jam-web/log/unicorn.stderr.log" -stdout_path "/var/lib/jam-web/log/unicorn.stdout.log" +#stderr_path "/var/lib/jam-web/log/unicorn.stderr.log" +#stdout_path "/var/lib/jam-web/log/unicorn.stdout.log" # combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings # http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow @@ -95,7 +95,11 @@ after_fork do |server, worker| ActiveRecord::Base.establish_connection Thread.new do - JamWebEventMachine.run_em + begin + JamWebEventMachine.run_em + rescue Exception => e + puts "unable to start eventmachine in after_fork!!: #{e}" + end end # if preload_app is true, then you may also want to check and # restart any other shared sockets/descriptors such as Memcached, From bc006b8c71ea8bb10875901fae5cbf887416f34a Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 23 Apr 2014 19:48:40 +0000 Subject: [PATCH 17/21] * adding some error logs for recording missing scenarios (VRFS-1643), and merging email redirect problem manually --- admin/app/admin/email_batch.rb | 6 ++++-- web/app/assets/javascripts/recordingFinishedDialog.js | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/admin/app/admin/email_batch.rb b/admin/app/admin/email_batch.rb index d074fd0d4..e5743eeb8 100644 --- a/admin/app/admin/email_batch.rb +++ b/admin/app/admin/email_batch.rb @@ -97,12 +97,14 @@ ActiveAdmin.register JamRuby::EmailBatch, :as => 'Batch Emails' do def create batch = EmailBatch.create_with_params(params[:jam_ruby_email_batch]) - redirect_to admin_batch_email_path(batch.id) + set_resource_ivar(batch) + render active_admin_template('show') end def update resource.update_with_conflict_validation(params[:jam_ruby_email_batch]) - redirect_to admin_batch_email_path(resource.id) + set_resource_ivar(resource) + render active_admin_template('show') end end diff --git a/web/app/assets/javascripts/recordingFinishedDialog.js b/web/app/assets/javascripts/recordingFinishedDialog.js index 688b6deef..90061b63e 100644 --- a/web/app/assets/javascripts/recordingFinishedDialog.js +++ b/web/app/assets/javascripts/recordingFinishedDialog.js @@ -36,8 +36,8 @@ var localResults = context.jamClient.GetLocalRecordingState({recordings: [recording]}); - if (localResults['error']) { + logger.error("unable to open recording due to error: %o", localResults); app.notify({ title: "Unable to Open Recording for Playback", text: localResults['error'], @@ -47,6 +47,7 @@ else { var localResult = localResults.recordings[0]; if (localResult.aggregate_state == 'MISSING') { + logger.error("unable to open recording due to all missing tracks: %o", localResults); app.notify({ title: "Unable to Open Recording for Playback", text: "All tracks associated with the recording are missing", @@ -54,6 +55,7 @@ }); } else if (localResult.aggregate_state == 'PARTIALLY_MISSING') { + logger.error("unable to open recording due to some missing tracks: %o", localResults); app.notify({ title: "Unable to Open Recording for Playback", text: "Some tracks associated with the recording are missing", From f9e035e62156c7c379bcc953b452eff4e47525c8 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 23 Apr 2014 21:52:35 +0000 Subject: [PATCH 18/21] * adding db 1/0 to test mode --- admin/config/environments/test.rb | 2 ++ web/config/environments/test.rb | 2 ++ 2 files changed, 4 insertions(+) diff --git a/admin/config/environments/test.rb b/admin/config/environments/test.rb index 6a51a23eb..1f9229001 100644 --- a/admin/config/environments/test.rb +++ b/admin/config/environments/test.rb @@ -40,4 +40,6 @@ JamAdmin::Application.configure do config.twitter_app_id = 'e7hGc71gmcBgo6Wvdta6Sg' config.twitter_app_secret = 'PfG1jAUMnyrimPcDooUVQaJrG1IuDjUyGg5KciOo' + + config.redis_host = "localhost:6379:1" # go to another db to not cross pollute into dev/production redis dbs end diff --git a/web/config/environments/test.rb b/web/config/environments/test.rb index b3f417864..e4dd042b0 100644 --- a/web/config/environments/test.rb +++ b/web/config/environments/test.rb @@ -51,6 +51,8 @@ SampleApp::Application.configure do # through jam-admin, then jam-web can point users at it. I think 99% of devs won't even see or care about this config, and 0% of users config.jam_admin_root_url = 'http://localhost:3333' + config.redis_host = "localhost:6379:1" # go to another db to not cross pollute into dev/production redis dbs + config.storage_type = :file config.aws_bucket = 'jamkazam-testing' From fb662e82bee122696f8dbd970bbfb1c76424960c Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 23 Apr 2014 22:03:00 +0000 Subject: [PATCH 19/21] * sessionModel to send out events on start/stop --- web/app/assets/javascripts/sessionModel.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/app/assets/javascripts/sessionModel.js b/web/app/assets/javascripts/sessionModel.js index 4cecf82bb..b84914e70 100644 --- a/web/app/assets/javascripts/sessionModel.js +++ b/web/app/assets/javascripts/sessionModel.js @@ -21,6 +21,7 @@ var currentTrackChanges = 0; // we track all the clientIDs of all the participants ever seen by this session, so that we can reliably convert a clientId from the backend into a username/avatar var participantsEverSeen = {}; + var $self = $(this); function id() { return currentSession ? currentSession.id : null; @@ -90,6 +91,7 @@ server.registerMessageCallback(context.JK.MessageType.SESSION_JOIN, trackChanges); server.registerMessageCallback(context.JK.MessageType.SESSION_DEPART, trackChanges); server.registerMessageCallback(context.JK.MessageType.TRACKS_CHANGED, trackChanges); + $self.trigger('jamkazam.session_started', {session: {id: sessionId}}); }) .fail(function() { currentSessionId = null; @@ -126,6 +128,7 @@ context.jamClient.SessionSetAlertCallback(""); context.jamClient.SessionSetConnectionStatusRefreshRate(0); updateCurrentSession(null); + $self.trigger('jamkazam.session_stopped', {session: {id: currentSessionId}}); currentSessionId = null; } /** From 2496c051a39023d9d7c19a1a26df2eba3e2b4804 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Wed, 23 Apr 2014 22:13:37 +0000 Subject: [PATCH 20/21] * doubting approach of using sessionModel as trigger handler; changing to document --- web/app/assets/javascripts/sessionModel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/assets/javascripts/sessionModel.js b/web/app/assets/javascripts/sessionModel.js index b84914e70..abdabe683 100644 --- a/web/app/assets/javascripts/sessionModel.js +++ b/web/app/assets/javascripts/sessionModel.js @@ -91,7 +91,7 @@ server.registerMessageCallback(context.JK.MessageType.SESSION_JOIN, trackChanges); server.registerMessageCallback(context.JK.MessageType.SESSION_DEPART, trackChanges); server.registerMessageCallback(context.JK.MessageType.TRACKS_CHANGED, trackChanges); - $self.trigger('jamkazam.session_started', {session: {id: sessionId}}); + $(document).trigger('jamkazam.session_started', {session: {id: sessionId}}); }) .fail(function() { currentSessionId = null; @@ -128,7 +128,7 @@ context.jamClient.SessionSetAlertCallback(""); context.jamClient.SessionSetConnectionStatusRefreshRate(0); updateCurrentSession(null); - $self.trigger('jamkazam.session_stopped', {session: {id: currentSessionId}}); + $(document).trigger('jamkazam.session_stopped', {session: {id: currentSessionId}}); currentSessionId = null; } /** From 26a9f9a1627ed2eeadbf74cdf96bb3996be62429 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 24 Apr 2014 02:27:31 +0000 Subject: [PATCH 21/21] * commenting 'Measurement Protocol' into related classes --- ruby/lib/jam_ruby/lib/google_analytics_tracker.rb | 1 + ruby/lib/jam_ruby/resque/google_analytics_event.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/ruby/lib/jam_ruby/lib/google_analytics_tracker.rb b/ruby/lib/jam_ruby/lib/google_analytics_tracker.rb index 85cb240c0..3603b4960 100644 --- a/ruby/lib/jam_ruby/lib/google_analytics_tracker.rb +++ b/ruby/lib/jam_ruby/lib/google_analytics_tracker.rb @@ -1,5 +1,6 @@ require 'rest_client' +# more info on Measurement Protocol https://developers.google.com/analytics/devguides/collection/protocol/v1/ class GoogleAnalyticsTracker attr_accessor :enabled, :tracking_code, :ga_version, :ga_endpoint diff --git a/ruby/lib/jam_ruby/resque/google_analytics_event.rb b/ruby/lib/jam_ruby/resque/google_analytics_event.rb index deb85cde1..58c393084 100644 --- a/ruby/lib/jam_ruby/resque/google_analytics_event.rb +++ b/ruby/lib/jam_ruby/resque/google_analytics_event.rb @@ -1,5 +1,6 @@ require 'resque' +# more info on Measurement Protocol https://developers.google.com/analytics/devguides/collection/protocol/v1/ module JamRuby class GoogleAnalyticsEvent