* lessons 90% done, but still mostly hidden

This commit is contained in:
Seth Call 2016-04-05 21:23:15 -05:00
parent 557bdc42db
commit 399d010310
309 changed files with 22940 additions and 1233 deletions

View File

@ -77,6 +77,9 @@ gem 'influxdb', '0.1.8'
gem 'influxdb-rails', '0.1.10'
gem 'recurly'
gem 'sendgrid_toolkit', '>= 1.1.1'
gem 'stripe'
gem 'zip-codes'
gem 'email_validator'
group :libv8 do
gem 'libv8', "~> 4.5.95"

View File

@ -0,0 +1,47 @@
ActiveAdmin.register JamRuby::LessonBooking, :as => 'LessonBookings' do
menu :label => 'LessonBooking', :parent => 'JamClass'
config.sort_order = 'created_at desc'
config.batch_actions = false
config.per_page = 100
config.paginate = true
config.filters = false
scope("All", default: true ) { |scope| scope.unscoped.order('created_at desc') }
scope("Requested") { |scope| scope.unscoped.where(status: LessonBooking::STATUS_REQUESTED).order('created_at desc') }
scope("Approved") { |scope| scope.unscoped.approved.order('created_at desc') }
scope("Suspended" ) { |scope| scope.unscoped.suspended.order('created_at desc') }
scope("Canceled" ) { |scope| scope.unscoped.canceled.order('created_at desc') }
index do
column "User Link" do |lesson_booking|
span do
link_to "Web URL", "#{Rails.application.config.external_root_url}/client#/jamclass/lesson-booking/#{lesson_booking.id}"
end
end
column "Type" do |lesson_booking|
lesson_booking.display_type
end
column "Status" do |lesson_booking|
lesson_booking.status
end
column "Teacher" do |lesson_booking|
teacher = lesson_booking.teacher
span do
link_to "#{teacher.name} (#{teacher.email})", "#{Rails.application.config.external_root_url}/client#/profile/teacher/#{teacher.id}"
end
end
column "Student" do |lesson_booking|
student = lesson_booking.student
span do
link_to "#{student.name} (#{student.email})", "#{Rails.application.config.external_root_url}/client#/profile/#{student.id}"
end
end
end
show do
end
end

View File

@ -0,0 +1,59 @@
ActiveAdmin.register JamRuby::LessonSession, :as => 'LessonSessions' do
menu :label => 'LessonSession', :parent => 'JamClass'
config.sort_order = 'created_at desc'
config.batch_actions = false
config.per_page = 100
config.paginate = true
config.filters = false
scope("All", default: true) { |scope| scope.unscoped.order('created_at desc') }
scope("Requested" ) { |scope| scope.unscoped.where(status: LessonBooking::STATUS_REQUESTED).order('created_at desc') }
scope("Approved") { |scope| scope.unscoped.approved.order('created_at desc') }
scope("Suspended" ) { |scope| scope.unscoped.suspended.order('created_at desc') }
scope("Canceled" ) { |scope| scope.unscoped.canceled.order('created_at desc') }
scope("Missed" ) { |scope| scope.unscoped.missed.order('created_at desc') }
scope("Completed" ) { |scope| scope.unscoped.completed.order('created_at desc') }
index do
column "User Link" do |lesson_session|
lesson_booking = lesson_session.lesson_booking
span do
link_to "Web URL", "#{Rails.application.config.external_root_url}/client#/jamclass/lesson-booking/#{lesson_booking.id}"
end
end
column "Status" do |lesson_session|
lesson_session.status
end
column "Start Time" do |lesson_session|
span do
lesson_session.music_session.pretty_scheduled_start(true)
end
br
span do
lesson_session.music_session.scheduled_start
end
end
column "Duration" do |lesson_session|
lesson_session.duration
end
column "Teacher" do |lesson_session|
teacher = lesson_session.teacher
span do
link_to "#{teacher.name} (#{teacher.email})", "#{Rails.application.config.external_root_url}/client#/profile/teacher/#{teacher.id}"
end
end
column "Student" do |lesson_session|
student = lesson_session.student
span do
link_to "#{student.name} (#{student.email})", "#{Rails.application.config.external_root_url}/client#/profile/#{student.id}"
end
end
end
show do
end
end

View File

@ -15,7 +15,6 @@ ActiveAdmin.register_page "Monthly Stats" do
column "Sessions", :count
end
h2 "Distinct Users Who Played with a JamTrack"
table_for MusicSession.select([:month, :count]).find_by_sql("select date_trunc('month', jts.created_at)::date as month, count(distinct(user_id)) from jam_track_sessions jts group by month order by month desc;") do
column "Month", Proc.new { |row| Date.parse(row.month).strftime('%B %Y') }
@ -36,5 +35,4 @@ ActiveAdmin.register_page "Monthly Stats" do
end
end

View File

@ -1,4 +1,3 @@
=begin
ActiveAdmin.register JamRuby::User, :as => 'Students' do
menu :label => 'Students', :parent => 'JamClass'
@ -9,66 +8,25 @@ ActiveAdmin.register JamRuby::User, :as => 'Students' do
config.paginate = true
def booked_anything(scope)
scope.joins(lesson_booking)
scope.joins(:student_lesson_bookings).where('lesson_bookings.active = true').uniq
end
scope("Default", default: true) { |scope| booked_anything(scope) }
scope("Default", default: true) { |scope| booked_anything(scope).order('ready_for_session_at IS NULL DESC') }
index do
column "Name" do |teacher|
link_to teacher.user.name, "#{Rails.application.config.external_root_url}/client#/profile/teacher/#{teacher.user.id}"
column "Name" do |user|
link_to user.name, "#{Rails.application.config.external_root_url}/client#/profile/#{user.id}"
end
column "Email" do |teacher|
teacher.user.email
column "Email" do |user|
user.email
end
column "Location" do |teacher|
teacher.user.location(country = true)
column "Location" do |user|
user.location(country = true)
end
column "Profile %" do |teacher|
column "Session Ready" do |user|
div do
span do
"#{teacher.pct_complete[:pct]}%"
end
br
span do
link_to "Detail", admin_teacher_path(teacher.id)
end
end
end
column "Background Check" do |teacher|
div do
if teacher.background_check_at
span do
teacher.background_check_at.to_date
end
span do
br
end
span do
link_to(mark_background_check_admin_teacher_path(teacher.id), {confirm: "Mark as background checked?"}) do
"mark as checked"
end
end
else
span do
'NOT DONE'
end
span do
br
end
span do
link_to("mark as checked", mark_background_check_admin_teacher_path(teacher.id), {confirm: "Mark as background checked?"})
end
end
end
end
column "Session Ready" do |teacher|
div do
if teacher.ready_for_session
if user.ready_for_session_at
span do
'YES'
end
@ -80,166 +38,22 @@ ActiveAdmin.register JamRuby::User, :as => 'Students' do
br
end
span do
link_to("mark as checked", mark_session_ready_admin_teacher_path(teacher.id), {confirm: "Mark as ready for session?"})
link_to("mark as checked", mark_session_ready_admin_student_path(user.id), {confirm: "Mark as ready for session?"})
end
end
end
end
column "Top Teacher" do |teacher|
div do
if teacher.top_rated
span do
'YES'
end
span do
br
end
span do
link_to("mark not top", mark_not_top_admin_teacher_path(teacher.id), {confirm: "Mark as not top rated?"})
end
else
span do
'NO'
end
span do
br
end
span do
link_to("mark as top", mark_top_admin_teacher_path(teacher.id), {confirm: "Mark as top rated?"})
end
end
end
end
end
show do
attributes_table do
row "Name" do |teacher|
link_to teacher.user.name, "#{Rails.application.config.external_root_url}/client#/profile/teacher/#{teacher.user.id}"
end
row "Email" do |teacher|
teacher.user.email
end
row "Location" do |teacher|
teacher.user.location(country = true)
end
row "Profile %" do |teacher|
div do
span do
"#{teacher.pct_complete[:pct]}%"
end
br
br
div do
h5 do "Completed Sections" end
teacher.pct_complete.each do |k, v|
if k != :pct && v
div do
k
end
end
end
br
br
h5 do "Uncompleted Sections" end
teacher.pct_complete.each do |k, v|
if k != :pct && !v
div do
k
end
end
end
end
end
end
row "Background Check" do |teacher|
div do
if teacher.background_check_at
span do
teacher.background_check_at.to_date
end
span do
br
end
span do
link_to(mark_background_check_admin_teacher_path(teacher.id), {confirm: "Mark as background checked?"}) do
"mark as checked"
end
end
else
span do
'NOT DONE'
end
span do
br
end
span do
link_to("mark as checked", mark_background_check_admin_teacher_path(teacher.id), {confirm: "Mark as background checked?"})
end
end
end
end
row "Session Ready" do |teacher|
div do
if teacher.ready_for_session
span do
'YES'
end
else
span do
'NO'
end
span do
br
end
span do
link_to("mark as checked", mark_session_ready_admin_teacher_path(teacher.id), {confirm: "Mark as ready for session?"})
end
end
end
end
row "Top Teacher" do |teacher|
div do
if teacher.top_rated
span do
'YES'
end
span do
br
end
span do
link_to("mark not top", mark_not_top_admin_teacher_path(teacher.id), {confirm: "Mark as not top rated?"})
end
else
span do
'NO'
end
span do
br
end
span do
link_to("mark as top", mark_top_admin_teacher_path(teacher.id), {confirm: "Mark as top rated?"})
end
end
end
column "School" do |user|
if teacher.school
teacher.school.name
end
end
end
member_action :mark_session_ready, :method => :get do
resource.mark_session_ready
redirect_to :back
end
end
=end
end

View File

@ -45,6 +45,7 @@ ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do
end
end
=begin
column "Background Check" do |teacher|
div do
if teacher.background_check_at
@ -73,6 +74,7 @@ ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do
end
end
end
=end
column "Session Ready" do |teacher|
div do
if teacher.ready_for_session_at
@ -122,7 +124,11 @@ ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do
column "Signed Up" do |teacher|
teacher.created_at.to_date
end
column "School" do |teacher|
if teacher.school
teacher.school.name
end
end
end
show do
@ -166,6 +172,7 @@ ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do
end
end
=begin
row "Background Check" do |teacher|
div do
if teacher.background_check_at
@ -196,6 +203,7 @@ ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do
end
end
=end
row "Session Ready" do |teacher|
div do
@ -248,6 +256,12 @@ ActiveAdmin.register JamRuby::Teacher, :as => 'Teachers' do
teacher.created_at.to_date
end
row "School" do |teacher|
if teacher.school
teacher.school.name
end
end
end
end

View File

@ -1,4 +1,6 @@
class ApplicationController < ActionController::Base
include ApplicationHelper
protect_from_forgery
before_filter :prepare_gon

View File

@ -1,4 +1,5 @@
module ApplicationHelper
end

View File

@ -340,4 +340,5 @@ jamblaster_pairing_active.sql
email_blacklist.sql
jamblaster_connection.sql
teacher_progression.sql
teacher_complete.sql
teacher_complete.sql
lessons.sql

261
db/up/lessons.sql Normal file
View File

@ -0,0 +1,261 @@
CREATE TABLE lesson_package_types (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR NOT NULL,
description VARCHAR NOT NULL,
package_type VARCHAR(64) NOT NULL,
price NUMERIC(8,2),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE lesson_bookings (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id VARCHAR(64) REFERENCES users(id) NOT NULL,
active BOOLEAN NOT NULL DEFAULT FALSE,
accepter_id VARCHAR(64) REFERENCES users(id),
canceler_id VARCHAR(64) REFERENCES users(id),
lesson_type VARCHAR(64) NOT NULL,
recurring BOOLEAN NOT NULL,
lesson_length INTEGER NOT NULL,
payment_style VARCHAR(64) NOT NULL,
description VARCHAR,
booked_price NUMERIC(8,2) NOT NULL,
teacher_id VARCHAR(64) REFERENCES users(id) NOT NULL,
card_presumed_ok BOOLEAN NOT NULL DEFAULT FALSE,
sent_notices BOOLEAN NOT NULL DEFAULT FALSE,
status VARCHAR,
cancel_message VARCHAR,
user_decremented BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE charges (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
amount_in_cents INTEGER NOT NULL,
fee_in_cents INTEGER NOT NULL DEFAULT 0,
type VARCHAR(64) NOT NULL,
sent_billing_notices BOOLEAN NOT NULL DEFAULT FALSE,
sent_billing_notices_at TIMESTAMP,
last_billing_attempt_at TIMESTAMP,
billed BOOLEAN NOT NULL DEFAULT FALSE,
billed_at TIMESTAMP,
post_processed BOOLEAN NOT NULL DEFAULT FALSE,
post_processed_at TIMESTAMP,
billing_error_reason VARCHAR,
billing_error_detail VARCHAR,
billing_should_retry BOOLEAN NOT NULL DEFAULT TRUE ,
billing_attempts INTEGER NOT NULL DEFAULT 0,
stripe_charge_id VARCHAR(200),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE lesson_package_purchases (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
lesson_package_type_id VARCHAR(64) REFERENCES lesson_package_types(id) NOT NULL,
user_id VARCHAR(64) REFERENCES users(id) NOT NULL,
teacher_id VARCHAR(64) REFERENCES users(id),
price NUMERIC(8,2),
recurring BOOLEAN NOT NULL DEFAULT FALSE,
year INTEGER,
month INTEGER,
charge_id VARCHAR(64) REFERENCES charges(id),
lesson_booking_id VARCHAR(64) REFERENCES lesson_bookings(id),
sent_notices BOOLEAN NOT NULL DEFAULT FALSE,
sent_notices_at TIMESTAMP,
post_processed BOOLEAN NOT NULL DEFAULT FALSE,
post_processed_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE lesson_sessions (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
lesson_type VARCHAR(64) NOT NULL,
teacher_id VARCHAR(64) REFERENCES users(id) NOT NULL,
lesson_package_purchase_id VARCHAR(64) REFERENCES lesson_package_purchases(id),
lesson_booking_id VARCHAR(64) REFERENCES lesson_bookings(id),
duration INTEGER NOT NULL,
booked_price NUMERIC(8,2) NOT NULL,
teacher_complete BOOLEAN DEFAULT FALSE NOT NULL,
student_complete BOOLEAN DEFAULT FALSE NOT NULL,
student_canceled BOOLEAN DEFAULT FALSE NOT NULL,
teacher_canceled BOOLEAN DEFAULT FALSE NOT NULL,
student_canceled_at TIMESTAMP,
teacher_canceled_at TIMESTAMP,
student_canceled_reason VARCHAR,
teacher_canceled_reason VARCHAR,
status VARCHAR,
analysed BOOLEAN NOT NULL DEFAULT FALSE,
analysis JSON,
analysed_at TIMESTAMP,
cancel_message VARCHAR,
canceler_id VARCHAR(64) REFERENCES users(id),
charge_id VARCHAR(64) REFERENCES charges(id),
success BOOLEAN NOT NULL DEFAULT FALSE,
sent_notices BOOLEAN NOT NULL DEFAULT FALSE,
sent_notices_at TIMESTAMP,
post_processed BOOLEAN NOT NULL DEFAULT FALSE,
post_processed_at TIMESTAMP,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE music_sessions ADD COLUMN lesson_session_id VARCHAR(64) REFERENCES lesson_sessions(id);
ALTER TABLE notifications ADD COLUMN lesson_session_id VARCHAR(64) REFERENCES lesson_sessions(id);
ALTER TABLE notifications ADD COLUMN purpose VARCHAR(200);
ALTER TABLE notifications ADD COLUMN student_directed BOOLEAN;
INSERT INTO lesson_package_types (id, name, description, package_type, price) VALUES ('single', 'Single Lesson', 'A single lesson purchased at the teacher''s price.', 'single', 0.00);
INSERT INTO lesson_package_types (id, name, description, package_type, price) VALUES ('single-free', 'Free Lesson', 'A free, single lesson.', 'single-free', 0.00);
INSERT INTO lesson_package_types (id, name, description, package_type, price) VALUES ('test-drive', 'Test Drive', 'Four reduced-price lessons which you can use to find that ideal teacher.', 'test-drive', 49.99);
CREATE TABLE lesson_booking_slots (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
lesson_booking_id VARCHAR(64) REFERENCES lesson_bookings(id),
lesson_session_id VARCHAR(64) REFERENCES lesson_sessions(id),
slot_type VARCHAR(64) NOT NULL,
preferred_day DATE,
day_of_week INTEGER,
hour INTEGER,
minute INTEGER,
timezone VARCHAR NOT NULL,
message VARCHAR,
accept_message VARCHAR,
update_all BOOLEAN NOT NULL DEFAULT FALSE,
proposer_id VARCHAR(64) REFERENCES users(id) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE lesson_bookings ADD COLUMN default_slot_id VARCHAR(64) REFERENCES lesson_booking_slots(id);
ALTER TABLE lesson_bookings ADD COLUMN counter_slot_id VARCHAR(64) REFERENCES lesson_booking_slots(id);
ALTER TABLE lesson_sessions ADD COLUMN counter_slot_id VARCHAR(64) REFERENCES lesson_booking_slots(id);
ALTER TABLE lesson_sessions ADD COLUMN slot_id VARCHAR(64) REFERENCES lesson_booking_slots(id);
ALTER TABLE chat_messages ADD COLUMN target_user_id VARCHAR(64) REFERENCES users(id);
ALTER TABLE chat_messages ADD COLUMN lesson_booking_id VARCHAR(64) REFERENCES lesson_bookings(id);
ALTER TABLE users ADD COLUMN remaining_free_lessons INTEGER NOT NULL DEFAULT 1;
ALTER TABLE users ADD COLUMN stored_credit_card BOOLEAN NOT NULL DEFAULT FALSE;
ALTER TABLE users ADD COLUMN remaining_test_drives INTEGER NOT NULL DEFAULT 0;
ALTER TABLE users ADD COLUMN stripe_token VARCHAR(200);
ALTER TABLE users ADD COLUMN stripe_customer_id VARCHAR(200);
ALTER TABLE users ADD COLUMN stripe_zip_code VARCHAR(200);
ALTER TABLE sales ADD COLUMN stripe_charge_id VARCHAR(200);
ALTER TABLE teachers ADD COLUMN stripe_account_id VARCHAR(200);
ALTER TABLE sale_line_items ADD COLUMN lesson_package_purchase_id VARCHAR(64) REFERENCES lesson_package_purchases(id);
-- one is created every time the teacher is paid. N teacher_distributions point to this
CREATE TABLE teacher_payments (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
teacher_id VARCHAR(64) REFERENCES users(id) NOT NULL,
charge_id VARCHAR(64) REFERENCES charges(id) NOT NULL,
amount_in_cents INTEGER NOT NULL,
fee_in_cents INTEGER NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- one is created for every bit of money the teacher is due
CREATE TABLE teacher_distributions (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
teacher_id VARCHAR(64) REFERENCES users(id) NOT NULL,
teacher_payment_id VARCHAR(64) REFERENCES teacher_payments(id),
lesson_session_id VARCHAR(64) REFERENCES lesson_sessions(id),
lesson_package_purchase_id VARCHAR(64) REFERENCES lesson_package_purchases(id),
amount_in_cents INTEGER NOT NULL,
ready BOOLEAN NOT NULL DEFAULT FALSE,
distributed BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE affiliate_distributions (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
affiliate_referral_id INTEGER REFERENCES affiliate_partners(id) NOT NULL,
affiliate_referral_fee_in_cents INTEGER NOT NULL,
sale_line_item_id VARCHAR(64) REFERENCES sale_line_items(id) NOT NULL,
affiliate_refunded BOOLEAN NOT NULL DEFAULT FALSE,
affiliate_refunded_at TIMESTAMP WITHOUT TIME ZONE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE affiliate_partners ADD COLUMN lesson_rate NUMERIC (8,2) NOT NULL DEFAULT 0.20;
-- move over all sale_line_item affiliate info
INSERT INTO affiliate_distributions (
SELECT
sale_line_items.id,
sale_line_items.affiliate_referral_id,
sale_line_items.affiliate_referral_fee_in_cents,
sale_line_items.id,
sale_line_items.affiliate_refunded,
sale_line_items.affiliate_refunded_at,
sale_line_items.created_at,
sale_line_items.updated_at
FROM sale_line_items
WHERE sale_line_items.affiliate_referral_id IS NOT NULL
);
CREATE TABLE teacher_intents (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id VARCHAR(64) REFERENCES users(id) NOT NULL,
teacher_id VARCHAR(64) REFERENCES teachers(id) NOT NULL,
intent VARCHAR(64),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX teacher_intents_intent_idx ON teacher_intents(teacher_id, intent);
CREATE TABLE schools (
id INTEGER PRIMARY KEY,
user_id VARCHAR(64) REFERENCES users(id) NOT NULL,
name VARCHAR,
enabled BOOLEAN DEFAULT TRUE,
scheduling_communication VARCHAR NOT NULL DEFAULT 'teacher',
correspondence_email VARCHAR,
photo_url VARCHAR(2048),
original_fpfile VARCHAR(8000),
cropped_fpfile VARCHAR(8000),
cropped_s3_path VARCHAR(8000),
crop_selection VARCHAR(256),
large_photo_url VARCHAR(512),
cropped_large_s3_path VARCHAR(512),
cropped_large_fpfile VARCHAR(8000),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE SEQUENCE school_key_sequence;
ALTER SEQUENCE school_key_sequence RESTART WITH 10000;
ALTER TABLE schools ALTER COLUMN id SET DEFAULT nextval('school_key_sequence');
ALTER TABLE users ADD COLUMN school_id INTEGER REFERENCES schools(id);
ALTER TABLE users ADD COLUMN joined_school_at TIMESTAMP;
ALTER TABLE teachers ADD COLUMN school_id INTEGER REFERENCES schools(id);
ALTER TABLE teachers ADD COLUMN joined_school_at TIMESTAMP;
CREATE TABLE school_invitations (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id VARCHAR(64) REFERENCES users(id),
school_id INTEGER REFERENCES schools(id) NOT NULL,
invitation_code VARCHAR(256) NOT NULL UNIQUE,
note VARCHAR,
as_teacher BOOLEAN NOT NULL,
email VARCHAR NOT NULL,
first_name VARCHAR,
last_name VARCHAR,
accepted BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
ALTER TABLE teachers ADD jamkazam_rate NUMERIC (8, 2) DEFAULT 0.25;
ALTER TABLE schools ADD jamkazam_rate NUMERIC (8, 2) DEFAULT 0.25;

View File

@ -92,6 +92,9 @@ message ClientMessage {
MIXDOWN_SIGN_COMPLETE = 270;
MIXDOWN_SIGN_FAILED = 271;
LESSON_MESSAGE = 280;
SCHEDULED_JAMCLASS_INVITATION = 281;
TEST_SESSION_MESSAGE = 295;
PING_REQUEST = 300;
@ -211,6 +214,9 @@ message ClientMessage {
optional MixdownSignComplete mixdown_sign_complete = 270;
optional MixdownSignFailed mixdown_sign_failed = 271;
// lesson notifications
optional LessonMessage lesson_message = 280;
optional ScheduledJamclassInvitation scheduled_jamclass_invitation = 281;
// Client-Session messages (to/from)
optional TestSessionMessage test_session_message = 295;
@ -678,6 +684,31 @@ message MixdownSignFailed {
required string mixdown_package_id = 1; // jam track mixdown package id
}
message LessonMessage {
optional string music_session_id = 1;
optional string photo_url = 2;
optional string msg = 3;
optional string notification_id = 4;
optional string created_at = 5;
optional string sender_id = 6;
optional string receiver_id = 7;
optional bool student_directed = 8;
optional string purpose = 9;
optional string sender_name = 10;
optional string lesson_session_id = 11;
}
message ScheduledJamclassInvitation {
optional string session_id = 1;
optional string photo_url = 2;
optional string msg = 3;
optional string session_name = 4;
optional string session_date = 5;
optional string notification_id = 6;
optional string created_at = 7;
optional string lesson_session_id = 8;
}
message SubscriptionMessage {
optional string type = 1; // the type of the subscription

View File

@ -52,6 +52,10 @@ gem 'sanitize'
gem 'influxdb', '0.1.8'
gem 'recurly'
gem 'sendgrid_toolkit', '>= 1.1.1'
gem 'stripe'
gem 'zip-codes'
gem 'icalendar'
gem 'email_validator'
group :test do
gem 'simplecov', '~> 0.7.1'
@ -66,7 +70,8 @@ group :test do
gem 'rspec-prof'
gem 'time_difference'
gem 'byebug'
gem 'icalendar'
gem 'stripe-ruby-mock'
end
# Specify your gem's dependencies in jam_ruby.gemspec

View File

@ -21,6 +21,9 @@ require 'rest-client'
require 'zip'
require 'csv'
require 'tzinfo'
require 'stripe'
require 'zip-codes'
require 'email_validator'
require "jam_ruby/constants/limits"
require "jam_ruby/constants/notification_types"
@ -53,6 +56,7 @@ 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/hourly_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"
@ -268,10 +272,27 @@ require "jam_ruby/models/gift_card"
require "jam_ruby/models/gift_card_purchase"
require "jam_ruby/models/gift_card_type"
require "jam_ruby/models/jam_track_session"
require "jam_ruby/models/lesson_package_type"
require "jam_ruby/models/lesson_package_purchase"
require "jam_ruby/models/lesson_session"
require "jam_ruby/models/lesson_booking"
require "jam_ruby/models/lesson_booking_slot"
require "jam_ruby/models/jamblaster"
require "jam_ruby/models/jamblaster_user"
require "jam_ruby/models/jamblaster_pairing_request"
require "jam_ruby/models/sale_receipt_ios"
require "jam_ruby/models/lesson_session_analyser"
require "jam_ruby/models/lesson_session_monthly_price"
require "jam_ruby/models/teacher_distribution"
require "jam_ruby/models/teacher_payment"
require "jam_ruby/models/charge"
require "jam_ruby/models/teacher_payment_charge"
require "jam_ruby/models/affiliate_payment_charge"
require "jam_ruby/models/lesson_payment_charge"
require "jam_ruby/models/affiliate_distribution"
require "jam_ruby/models/teacher_intent"
require "jam_ruby/models/school"
require "jam_ruby/models/school_invitation"
include Jampb

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
<% provide(:title, @subject) %>
Hello <%= @school_invitation.first_name %> -
<p><%= @school.owner.first_name %> is using JamKazam to deliver online music lessons, and has sent you this invitation so that you can
register to take online music lessons with <%= @school.name %>. To accept this invitation, please click the SIGN UP NOW
button below, and follow the instructions on the web page to which you are taken. Thanks, and on behalf of
<%= @school.name %>, welcome to JamKazam!</p>
<br/>
<p>
<a href="<%= @school_invitation.generate_signup_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">SIGN
UP NOW</a>
</p>
<br/>
<br/>
Best Regards,<br>
Team JamKazam

View File

@ -0,0 +1,12 @@
<% provide(:title, @subject) %>
Hello <%= @school_invitation.first_name %> -
<%= @school.owner.first_name %> is using JamKazam to deliver online music lessons, and has sent you this invitation so that you can
register to take online music lessons with <%= @school.name %>. To accept this invitation, please click the link
below, and follow the instructions on the web page to which you are taken. Thanks, and on behalf of
<%= @school.name %>, welcome to JamKazam!
<%= @school_invitation.generate_signup_url %>
Best Regards,<br>
Team JamKazam

View File

@ -0,0 +1,18 @@
<% provide(:title, @subject) %>
Hello <%= @school_invitation.first_name %> -
<br/>
<p>
<%= @school.owner.first_name %> has set up <%= @school.name %> on JamKazam, enabling you to deliver online music
lessons in an amazing new way that really works. To accept this invitation, please click the SIGN UP NOW button below,
and follow the instructions on the web page to which you are taken. Thanks, and welcome to JamKazam!</p>
<br/>
<p>
<a href="<%= @school_invitation.generate_signup_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">SIGN
UP NOW</a>
</p>
<br/>
<br/>
Best Regards,<br>
Team JamKazam

View File

@ -0,0 +1,11 @@
<% provide(:title, @subject) %>
Hello <%= @school_invitation.first_name %> -
<%= @school.owner.first_name %> has set up <%= @school.name %> on JamKazam, enabling you to deliver online music
lessons in an amazing new way that really works. To accept this invitation, please click the link below,
and follow the instructions on the web page to which you are taken. Thanks, and welcome to JamKazam!
<%= @school_invitation.generate_signup_url %>
Best Regards,
Team JamKazam

View File

@ -0,0 +1,28 @@
<% provide(:title, "Your JamClass lesson today with #{@teacher.first_name}") %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @student.name %>,
</p>
<p>
We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. As just a reminder, you already paid for this lesson in advance.
</p>
<p>
<% if !@student.has_rated_teacher(@teacher) %>
If you haven't already done so, please <a href="<%= @teacher.ratings_url %>" style="color:#fc0">rate your teacher</a> now to help other students in the community find the best
instructors.
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions
on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<br/>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,28 @@
<% provide(:title, "Your JamClass lesson today with #{@teacher.first_name}") %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @student.name %>,
</p>
<p>
We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. As just a reminder, you already paid for this lesson in advance.
</p>
<p>
<% if !@student.has_rated_teacher(@teacher) %>
If you haven't already done so, please <a href="<%= @teacher.ratings_url %>" style="color:#fc0">rate your teacher</a> now to help other students in the community find the best
instructors.
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions
on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<br/>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,23 @@
<% provide(:title, "Your lesson with #{@teacher.name} will not be billed") %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @student.name %>,
</p>
<p>You will not be billed for today's session with <%= @teacher.name %>. However, you already paid for the lesson in advance, so next month's bill will be lower than usual.
<br/>
<br/>
Click the button below to see more information about this session.
</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW
LESSON DETAILS</a>
</p>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,5 @@
Hello <%= @student.name %>,
You will not be billed for today's session with <%= @teacher.name %>. However, you already paid for the lesson in advance, so next month's bill will be lower than usual.
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,24 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_session.teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
<% if @slot.is_teacher_approved? %>
This teacher has accepted your lesson request!
<% else %>
You have confirmed a lesson request.
<% end %>
<% if @message.present? %>
<br/><br/><%= @sender.name %> says:
<br/><%= @message %>
<br/>
<% end %>
<br/><br/>Click the button below to get more information and to add this lesson to your calendar!
<br/><br/>We strongly suggest adding this to your calendar so you don't forget it, or you'll end up paying for a lesson you don't get.</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
<%= @subject %>
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,29 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_booking.canceler.resolved_photo_url) %>
<% content_for :note do %>
<p>
<% if @lesson_booking.recurring %>
All lessons that were scheduled for <%= @lesson_booking.dayWeekDesc %> with <%= @teacher.name %> have been canceled.
<% else %>
Your lesson with <%= @teacher.name %> has been canceled.<br/><br/>
Session Name: <%= @session_name %><br/>
Session Description: <%= @session_description %></br>
<%= @session_date %>
<% end %>
<% if @message.present? %>
<br/><br/><%= @lesson_booking.canceler.name %> says:
<br/><%= @message %>
<br/>
<% end %>
<br/><br/>Click the button below to view more info about the canceled session.</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
<%= @subject %>
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,20 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
This teacher has declined your lesson request.
<% if @message.present? %>
<br/><br/><%= @teacher.name %> says:
<br/><%= @message %>
<br/>
<% end %>
<br/><br/>Click the button below to view the teacher's response.</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW RESPONSE</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
<%= @subject %>
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,23 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_session.canceler.resolved_photo_url) %>
<% content_for :note do %>
<p>
Your lesson with <%= @teacher.name %> has been canceled.<br/><br/>
Session Name: <%= @session_name %><br/>
Session Description: <%= @session_description %></br>
<%= @session_date %>
<% if @message.present? %>
<br/><br/><%= @lesson_session.canceler.name %> says:
<br/><%= @message %>
<br/>
<% end %>
<br/><br/>Click the button below to view more info about the canceled session.</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW RESPONSE</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
<%= @subject %>
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,17 @@
<% provide(:title, "#{@teacher.name} has proposed a different time for your lesson") %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
<%= @teacher.name %> has proposed a different time for your lesson request.
<br/>
<br/>
Click the button below to get more information and respond.
</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW
LESSON DETAILS</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
<%= @teacher.name %> has proposed a different time for your lesson request.
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,28 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @student.name %>,
</p>
<p>
You have been billed $<%= @lesson_package_purchase.amount_charged %> for this month's lessons with <%= @teacher.name %>.
</p>
<p>
<% if !@student.has_rated_teacher(@teacher) %>
If you haven't already done so, please <a href="<%= @teacher.ratings_url %>" style="color:#fc0">rate your teacher</a> now to help other students in the community find the best
instructors.
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions
on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<br/>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,14 @@
Hello <%= @student.name %>,
You have been billed $<%= @lesson_package_purchase.amount_charged %> for this month's lessons with <%= @teacher.name %>.
<% if !@student.has_rated_teacher(@teacher) %>
If you haven't already done so, please rate your teachernow to help other students in the community find the best instructors. <%= @teacher.ratings_url %>
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions
on how to improve JamClass, please email us at support@jamkazam.com.
Best Regards,
Team JamKazam

View File

@ -0,0 +1,28 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @student.name %>,
</p>
<p>
You have been billed $<%= @lesson_package_purchase.amount_charged %> for this month's lessons with <%= @teacher.name %>.
</p>
<p>
<% if !@student.has_rated_teacher(@teacher) %>
If you haven't already done so, please <a href="<%= @teacher.ratings_url %>" style="color:#fc0">rate your teacher</a> now to help other students in the community find the best
instructors.
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions
on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<br/>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,29 @@
<% provide(:title, "Your JamClass lesson today with #{@teacher.first_name}") %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @student.name %>,
</p>
<p>
We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. You have been
billed $<%= @lesson_session.amount_charged %> for today's lesson.
</p>
<p>
<% if !@student.has_rated_teacher(@teacher) %>
If you haven't already done so, please <a href="<%= @teacher.ratings_url %>" style="color:#fc0">rate your teacher</a> now to help other students in the community find the best
instructors.
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions
on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<br/>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,16 @@
Hello <%= @student.name %>,
We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. You have been billed $<%= @lesson_session.amount_charged %> for today's lesson.
<% if !@student.has_rated_teacher(@teacher) %>
If you haven't already done so, please rate your teacher now to help other students in the community find the best
instructors. You can rate your teacher here: <%= @teacher.ratings_url %>
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions
on how to improve JamClass, please email us at support@jamkazam.com.
Best Regards,
Team JamKazam

View File

@ -0,0 +1,23 @@
<% provide(:title, "Your lesson with #{@teacher.name} will not be billed") %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @student.name %>,
</p>
<p>You will not be billed for today's session with <%= @teacher.name %>
<br/>
<br/>
Click the button below to see more information about this session.
</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW
LESSON DETAILS</a>
</p>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,5 @@
Hello <%= @student.name %>,
You will not be billed for today's session with <%= @teacher.name %>
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,11 @@
<% provide(:title, "Lesson requested of #{@sender.name}") %>
<% provide(:photo_url, @sender.resolved_photo_url) %>
<% content_for :note do %>
<p>You have requested a <%= @lesson_booking.display_type %> lesson. <br /><br/>Click the button below to see your lesson request. You will receive another email when the teacher accepts or rejects the request.</p>
<p>
<a href="<%= @lesson_booking.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON REQUEST</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
You have requested a lesson from <%= @sender.name %>.
To see this lesson request, click here: <%= @lesson_booking.web_url %>

View File

@ -0,0 +1,20 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_session.teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
All lessons with <%= @lesson_session.teacher.name %> have been rescheduled.
<% if @message.present? %>
<br/><br/><%= @sender.name %> says:
<br/><%= @message %>
<br/>
<% end %>
<br/><br/>Click the button below to get more information and to update your calendar!
<br/><br/>We strongly suggest adding this to your calendar so you don't forget it, or you'll end up paying for a lesson you don't get.</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
All your lessons with <%= @lesson_session.teacher.name%> have been rescheduled.
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,11 @@
<% provide(:title, @subject) %>
<p><%= @body %></p>
<p>
Session Name: <%= @session_name %><br/>
<%= @session_description %></br>
<%= @session_date %>
</p>
<p><a style="color: #ffcc00;" href="<%= @session_url %>">VIEW LESSON DETAILS</a></p>

View File

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

View File

@ -0,0 +1,30 @@
<% provide(:title, "You have used #{@student.remaining_test_drives} of 4 TestDrive lesson credits") %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @student.name %>,
</p>
<p>We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. You have
used <%= @student.used_test_drives %> TestDrive credits, and you have <%= @student.remaining_test_drives %>
remaining TestDrive lesson(s) available. If you havent booked your next TestDrive lesson,
<a href="<%= User.search_url %>" style="color:#fc0">click here</a> to search our teachers and get your next
lesson lined up today!</p>
<p>
<% if !@student.has_rated_teacher(@teacher) %>
Also, please <a href="<%= @teacher.ratings_url %>" style="color:#fc0">rate your teacher</a> now for todays lesson
to help other students in the community find the best instructors.
<% end %>
And if you had technical problems during your lesson, or have questions, or would like to make suggestions on how to
improve JamClass,
please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<br/>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,14 @@
You have used <%= @student.remaining_test_drives %> of 4 TestDrive lesson credits.
<% if @student.has_rated_teacher(@teacher) %>
Also, please rate your teacher at <%= @teacher.ratings_url %> now for todays lesson to help other students in the community find the best instructors.
<% end %>
If you clicked with one of your TestDrive instructors, here are links to each teachers listing. You can use the
link to your favorite to book single or weekly recurring lessons with the best instructor for you!
<% @student.recent_test_drive_teachers.each do |teacher| %>
<%= teacher.name %>: <%= teacher.teacher_profile_url %>
<% end %>
And if you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass,
please email us at support@jamkazam.com.

View File

@ -0,0 +1,47 @@
<% provide(:title, "You have used all 4 TestDrive lesson credits") %>
<p>
Hello <%= @student.name %>,
</p>
<p>
We hope you enjoyed your JamClass lesson today with <%= @teacher.name %>. You have now used all 4 TestDrive credits.
<% if !@student.has_rated_teacher(@teacher) %>
Please <a href="<%= @teacher.ratings_url %>" style="color:#fc0">rate your teacher</a> now for todays lesson to
help other students in the community find the best instructors.
<% end %>
</p>
<p>
If you clicked with one of your TestDrive instructors, here are links to each teachers listing. You can use the
link to your favorite to book single or weekly recurring lessons with the best instructor for you!
</p>
<% @student.recent_test_drive_teachers.each do |teacher| %>
<table>
<tr>
<td width="58" align="left" valign="top">
<p style="padding:2px;width:54px;height:54px;background-color:#ed3618;-webkit-border-radius:28px;-moz-border-radius:28px;border-radius:28px;margin-right:10px">
<img src="<%= teacher.resolved_photo_url %>" width="54" height="54" style="-webkit-border-radius:26px;-moz-border-radius:26px;border-radius:26px;">
</p></td>
<td><p>
<font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><a href="<%= teacher.teacher_profile_url %>"><%= teacher.name %></a></font>
</p>
</td>
</tr>
</table>
<% end %>
<p>
And if you had technical problems during your lesson, or have questions, or would like to make suggestions on how
to improve JamClass,
please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.</p>
<br/>
<p>
Best Regards,<br>Team JamKazam
</p>

View File

@ -0,0 +1,8 @@
You have used all of your 4 TestDrive lesson credits.
<% if @student.has_rated_teacher(@teacher) %>
Also, please rate your teacher at <%= @teacher.ratings_url %> now for todays lesson to help other students in the community find the best instructors.
<% end %>
And if you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass,
please email us at support@jamkazam.com.

View File

@ -0,0 +1,28 @@
<% provide(:title, "The credit card charge for your lesson today with #{@teacher.first_name} failed") %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @student.name %>,
</p>
<p>
<% if @card_declined %>
When we tried to charge your credit card for your lesson on <%= @bill_date %> with <%= @teacher.name %>, the charge was declined. Can you please call your credit card company to clear up the issue, or if you need to update some of your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% elsif @card_expired %>
When we tried to charge your credit card for your lesson on <%= @bill_date %> with <%= @teacher.name %>, the charge failed because the card is expired. To update your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% else %>
For some reason, when we tried to charge your credit card for your lesson on <%= @bill_date %> with <%= @teacher.name %>, the charge failed. Can you please call your credit card company to clear up the issue, or if you need to update some of your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% end %>
</p>
<p>
<a href="<%= @lesson_session.update_payment_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">UPDATE PAYMENT INFO</a>
</p>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,18 @@
The credit card charge for your lesson today with <%= @teacher.first_name %> failed.
Hello <%= @student.name %>,
<% if @card_declined %>
When we tried to charge your credit card for your lesson on <%= @bill_date %> with <%= @teacher.name %>, the charge was declined. Can you please call your credit card company to clear up the issue, or if you need to update some of your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% elsif @card_expired %>
When we tried to charge your credit card for your lesson on <%= @bill_date %> with <%= @teacher.name %>, the charge failed because the card is expired. To update your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% else %>
For some reason, when we tried to charge your credit card for your lesson on <%= @bill_date %> with <%= @teacher.name %>, the charge failed. Can you please call your credit card company to clear up the issue, or if you need to update some of your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% end %>
Update Payment info here: <%= @lesson_session.update_payment_url %>
Best Regards,
Team JamKazam

View File

@ -0,0 +1,32 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @teacher.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @student.name %>,
</p>
<p>
<% if @lesson_booking.is_suspended? %>
Your weekly lessons with <%= @teacher.name %> have been suspended because we have tried repeatedly to charge your credit card but have failed.
<br/>
<br/>
<% end %>
<% if @card_declined %>
When we tried to charge your credit card for your <%= @month_name %> lessons on <%= @bill_date %> with <%= @teacher.name %>, the charge was declined. Can you please call your credit card company to clear up the issue, or if you need to update some of your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% elsif @card_expired %>
When we tried to charge your credit card for your <%= @month_name %> lessons on <%= @bill_date %> with <%= @teacher.name %>, the charge failed because the card is expired. To update your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% else %>
For some reason, when we tried to charge your credit card for your <%= @month_name %> lessons on <%= @bill_date %> with <%= @teacher.name %>, the charge failed. Can you please call your credit card company to clear up the issue, or if you need to update some of your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% end %>
</p>
<p>
<a href="<%= @lesson_booking.update_payment_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">UPDATE PAYMENT INFO</a>
</p>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,23 @@
Hello <%= @student.name %>,
<%= @subject %>
<% if @lesson_booking.is_suspended? %>
Your weekly lessons with <%= @teacher.name %> have been suspended because we have tried repeatedly to charge your credit card but have failed.
<% end %>
<% if @card_declined %>
When we tried to charge your credit card for your <%= @month_name %> lessons on <%= @bill_date %> with <%= @teacher.name %>, the charge was declined. Can you please call your credit card company to clear up the issue, or if you need to update some of your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% elsif @card_expired %>
When we tried to charge your credit card for your <%= @month_name %> lessons on <%= @bill_date %> with <%= @teacher.name %>, the charge failed because the card is expired. To update your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% else %>
For some reason, when we tried to charge your credit card for your <%= @month_name %> lessons on <%= @bill_date %> with <%= @teacher.name %>, the charge failed. Can you please call your credit card company to clear up the issue, or if you need to update some of your credit card information, please use the button below to go to a page where you can change your card info. Thank you!
<% end %>
Update Payment info here: <%= @lesson_booking.update_payment_url %>
Best Regards,
Team JamKazam

View File

@ -0,0 +1,119 @@
<% provide(:title, 'Welcome to JamKazam!') %>
<% if !@user.anonymous? %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<% end %>
<p> We're delighted you have decided to join the JamKazam community of musicians, and we hope
you will enjoy using JamKazam and JamTracks to play more music. Following are some
resources and some things you can do to get the most out of JamKazam.
</p>
<p><b style="color: white;">Playing With JamTracks</b><br/>
JamTracks are the best way to play along with your favorite songs. Far better and different than
traditional backing tracks, our JamTracks are complete multi-track professional recordings, with
fully isolated tracks for each part of the music. And our free app and Internet service are packed
with features that give you unmatched creative freedom to learn, practice, record, play with
others, and share your performances. Here are some great JamTracks resources:
</p>
<ul>
<li><a style="color:#fc0" href="https://www.youtube.com/watch?v=07zJC7C2ICA">JamTracks Overview Video</a> - See all the great things you can do with JamTracks.</li>
<li><a style="color:#fc0" href="https://jamkazam.desk.com/customer/en/portal/articles/2124663-playing-with-jamtracks">JamTracks User Guide</a> - A set of articles that explain how to use all the JamTracks
features.</li>
<li><a style="color:#fc0" href="https://www.jamkazam.com/client#/jamtrack">Get a JamTrack Free</a> - A web page you can visit to search our catalog of JamTracks.
When you find a song you like, click the Get It Free button, and your first one is free! If
you already redeemed a free JamTrack or purchased JamTracks, you can also access
them on this page from your web browser.</li>
<li><a style="color:#fc0" href="https://www.jamkazam.com/downloads">JamKazam Application</a> - A web page where you can download our free Mac or Windows
app. The app lets you do a lot more with JamTracks than you can do in a browser.</li>
</ul>
<p><b style="color: white;">Play Live With Others from Different Locations on JamKazam</b><br/>
JamKazams free app lets musicians play together live and in sync from different locations over
the Internet. Kind of like Skype on super audio steroids, with ultra low latency and terrific audio
quality. You can set up online sessions that are public or private, for you alone or for others to
join. You can find and join others sessions, use backing tracks and loops in sessions, make
audio and video recordings of session performances, and more. <a style="color:#fc0" href="https://jamkazam.desk.com/customer/en/portal/topics/673198-tutorials-on-major-features/articles">Click here for a set of tutorial
videos that show how to use these features</a>.
</p>
<p><b style="color: white;">Teach or Take Online Music Lessons</b><br/>
If you teach music lessons and have tried to give lessons using Skype, youll know how
unsatisfactory that experience is. Audio quality is poor, and latency prohibits teacher and
student playing together at all. JamKazam is a terrific service for teaching and taking online
music lessons. If you want to use JamKazam for lessons, well be happy to support both you and
your students in getting set up and ready to go.
</p>
<p><b style="color: white;">Complete Your Profile</b><br/>
Every member of our community has a profile. Its a great way to share a little bit about who
you are as a musician, as well as your musical interests. For example, what instruments do you
play? What musical genres do you like best? Are you interested in getting into a virtual/online
or a real-world band? And so on. Filling out your profile will help you connect with others with
common interests. To do this, go to <a style="color:#fc0" href="https://www.jamkazam.com/client">www.jamkazam.com</a> or launch the JamKazam app. Then
click on the Profile tile, and click the Edit Profile button.
</p>
<p><b style="color: white;">Invite Your Friends</b><br/>
Have friends who are musicians? Invite them to join you to play together on JamKazam. To do
this, go to <a style="color:#fc0" href="https://www.jamkazam.com/client">www.jamkazam.com</a> or launch the JamKazam app. Then move your mouse over the
user icon in the top right corner of the screen. A menu will be displayed. Click Invite Friends in
this menu, and you can then use the options to invite your friends using their email addresses,
or via Facebook, or using your Google contacts.
<p>
<p><b style="color: white;">Get Help</b><br/>
If you run into trouble and need help, please reach out to us. We will be glad to do everything
we can to answer your questions and get you up and running. You can visit our <a style="color:#fc0" href="https://jamkazam.desk.com/">Support Portal</a>
to find knowledge base articles and post questions that have not already been answered. You
can email us at <a style="color:#fc0" href="mailto:support@jamkazam.com">support@jamkazam.com</a>. And if you just want to chat, share tips and war
stories, and hang out with fellow JamKazamers, you can visit our <a style="color:#fc0" href="http://forums.jamkazam.com/">Community Forum</a>.
<p>
<p>
<br/>
<br/>
Again, welcome to JamKazam, and we hope you have a great time here!
</p>
<p>Best Regards,<br/>
Team JamKazam</p>

View File

@ -0,0 +1,43 @@
<% if !@user.anonymous? %>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --<% end %>
We're delighted that you have decided to try the JamKazam service, and we hope that you will enjoy using JamKazam to play music with others. Following are some resources that can help you get oriented and get the most out of JamKazam.
Getting Started
---------------
There are basically three kinds of setups you can use to play on JamKazam.
* Built-In Audio on Your Computer - You can use a Windows or Mac computer, and just use the built-in mic and headphone jack to handle your audio. This is cheap and easy, but your audio quality will suffer, and it will also process audio very slowly, creating problems with latency, or lag, in your sessions. Still, you can at least start experimenting with JamKazam in this way.
* Computer with External Audio Interface - - You can use a Windows or Mac computer with an external audio interface that you already own and use for recording, if you happen to have one already. If you are going to do this, or use the built-in mic/headphones on your computer, please refer to our Minimum System Requirements at https://jamkazam.desk.com/customer/portal/articles/1288274-minimum-system-requirements to make sure your computer will work. These requirements were on the download page for the app, but you may have sped by them. Also, we'd recommend watching our Getting Started Video at https://www.youtube.com/watch?v=DBo--aj_P1w to learn more about your options here.
* The JamBlaster - JamKazam has designed a new product from the ground up to be the best way to play music online in real time. It's called the JamBlaster. It processes audio faster than any of the thousands of combinations of computers and interfaces in use on JamKazam today, which means you can play with musicians who are farther away from you, and closer sessions will feel/sound tighter. The JamBlaster is both a computer and an audio interface, so it also eliminates the system requirements worries, and it "just works" so you don't have to be an audio and computer genius to get it working. This is a great product - available only through a Kickstarter program running during a 30-day window during parts of February and March 2015. You can watch the JamBlaster Video at https://www.youtube.com/watch?v=gAJAIHMyois to learn more about this amazing new product.
JamKazam Features
-----------------
JamKazam offers a very robust and exciting set of features for playing online and sharing your performances with others. Here are some videos you can watch to easily get up to speed on some of the things you can do with JamKazam:
* Creating a Session - https://www.youtube.com/watch?v=EZZuGcDUoWk
* Finding a Session - https://www.youtube.com/watch?v=xWponSJo-GU
* Playing in a Session - https://www.youtube.com/watch?v=zJ68hA8-fLA
* Connecting with Other Musicians - https://www.youtube.com/watch?v=4KWklSZZxRc
* Working with Recordings - https://www.youtube.com/watch?v=Gn-dOqnNLoY
Getting Help
------------
If you run into trouble and need help, please reach out to us. We will be glad to do everything we can to answer your questions and get you up and running. You can visit our Support Portal at https://jamkazam.desk.com/ to find knowledge base articles and post questions that have not already been answered. You can email us at support@jamkazam.com. And if you just want to chat, share tips and war stories, and hang out with fellow JamKazamers, you can visit our Community Forum at http://forums.jamkazam.com/.
Again, welcome to JamKazam, and we look forward to seeing and hearing you online soon!
Best Regards,
Team JamKazam

View File

@ -0,0 +1,41 @@
<% provide(:title, @subject) %>
<p>You were paid a total of $<%= @teacher_payment.amount %> for your participation in JamClass. Below are more details:</p>
<br/>
<% @teacher_payment.teacher_distributions.each do |distribution| %>
<% if distribution.is_test_drive? %>
<h3>You have earned $<%= distribution.amount %> for your TestDrive lesson with <%= distribution.student.name %></h3>
<p>
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please <a href="<%= distribution.student.student_ratings_url %>" style="color:#fc0">rate your student</a> now to help us monitor for any issues with students who may cause issues for our instructor community.
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<% elsif distribution.is_normal? %>
<h3>You have earned $<%= distribution.amount %> for your lesson with <%= distribution.student.name %></h3>
<p>
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please <a href="<%= distribution.student.student_ratings_url %>" style="color:#fc0">rate your student</a> now to help us monitor for any issues with students who may cause issues for our instructor community.
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<% elsif distribution.is_monthly? %>
<h3>You have earned $<%= distribution.amount %> for your <%= distribution.month_name%> lesson with <%= distribution.student.name %></h3>
<p>
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please <a href="<%= distribution.student.student_ratings_url %>" style="color:#fc0">rate your student</a> now to help us monitor for any issues with students who may cause issues for our instructor community.
<% end %>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<% else %>
Unknown payment type.
<% end %>
<br/>
<br/>
<% end %>
Best Regards,<br/>
JamKazam

View File

@ -0,0 +1,31 @@
<% provide(:title, @subject) %>
You were paid a total of $<%= @teacher_payment.amount %> for your participation in JamClass. Below are more details:
<% @teacher_payment.teacher_distributions.each do |distribution| %>
<% if distribution.is_test_drive? %>
You have earned $<%= distribution.amount %> for your TestDrive lesson with <%= distribution.student.name %>.
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please rate your student now to help us monitor for any issues with students who may cause issues for our instructor community. <%= distribution.student.student_ratings_url %>
<% end%>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at support@jamkazam.com.
<% elsif distribution.is_normal? %>
You have earned $<%= distribution.amount %> for your lesson with <%= distribution.student.name %>.
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please rate your student now to help us monitor for any issues with students who may cause issues for our instructor community. <%= distribution.student.student_ratings_url %>
<% end%>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at support@jamkazam.com.
<% elsif distribution.is_monthly? %>
You have earned $<%= distribution.amount %> for your <%= distribution.month_name%> lesson with <%= distribution.student.name %>.
<% if !@teacher_payment.teacher.has_rated_student(distribution.student) %>
If you haven't already done so, please rate your student now to help us monitor for any issues with students who may cause issues for our instructor community. <%= distribution.student.student_ratings_url %>
<% end%>
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at support@jamkazam.com.
<% else %>
Unknown payment type.
<% end %>
<% end %>
Best Regards,
JamKazam

View File

@ -0,0 +1,15 @@
<% provide(:title, @subject) %>
<p>
<% if @card_declined %>
When we tried to distribute a payment to you on <%= @bill_date %>, the charge was declined by stripe. Can you please check your stripe account status? Thank you!
<% elsif @card_expired %>
When we tried to distribute a payment to you on <%= @bill_date %>, the charge was declined by stripe due to a card expiration. Can you please check your stripe account status? Thank you!
<% else %>
For some reason, when we tried to distribute a payment to you on <%= @bill_date %>, the charge failed. Can you please check your stripe account status? Thank you!
<% end %>
</p>
<br/>
Best Regards,<br/>
JamKazam

View File

@ -0,0 +1,12 @@
<% provide(:title, @subject) %>
<% if @card_declined %>
When we tried to distribute a payment to you on <%= @bill_date %>, the charge was declined by stripe. Can you please check your stripe account status? Thank you!
<% elsif @card_expired %>
When we tried to distribute a payment to you on <%= @bill_date %>, the charge was declined by stripe due to a card expiration. Can you please check your stripe account status? Thank you!
<% else %>
For some reason, when we tried to distribute a payment to you on <%= @bill_date %>, the charge failed. Can you please check your stripe account status? Thank you!
<% end %>
Best Regards,
JamKazam

View File

@ -0,0 +1,18 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_session.student.resolved_photo_url) %>
<% content_for :note do %>
<p>
<% if @slot.is_teacher_approved? %>
You have confirmed a lesson request.
<% else %>
This student has accepted your lesson request!
<% end %>
<br/><br/>Click the button below to get more information and to add this lesson to your calendar!
<br/><br/>We strongly suggest adding this to your calendar so you don't forget it.</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
You have confirmed a lesson request for <%= @sender.name %>.
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,29 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_booking.canceler.resolved_photo_url) %>
<% content_for :note do %>
<p>
<% if @lesson_booking.recurring %>
All lessons that were scheduled for <%= @lesson_booking.dayWeekDesc %> with <%= @student.name %> have been canceled.
<% else %>
Your lesson with <%= @student.name %> has been canceled.<br/><br/>
Session Name: <%= @session_name %><br/>
Session Description: <%= @session_description %></br>
<%= @session_date %>
<% end %>
<% if @message.present? %>
<br/><br/><%= @lesson_booking.canceler.name %> says:
<br/><%= @message %>
<br/>
<% end %>
<br/><br/>Click the button below to view more info about the canceled session.</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
<%= @subject %>
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,24 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_session.canceler.resolved_photo_url) %>
<% content_for :note do %>
<p>
Your lesson with <%= @teacher.name %> has been canceled.<br/><br/>
Session Name: <%= @session_name %><br/>
Session Description: <%= @session_description %></br>
<%= @session_date %>
<% if @message.present? %>
<br/><br/><%= @lesson_session.canceler.name %> says:
<br/><%= @message %>
<br/>
<% end %>
<br/><br/>Click the button below to view more info about the canceled session.</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
<%= @subject %>
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,17 @@
<% provide(:title, "You successfully completed a lesson with #{@student.name}") %>
<% provide(:photo_url, @student.resolved_photo_url) %>
<% content_for :note do %>
<p>
<%= @student.name %> will first be billed and you should receive your payment in the next 48 hours.
<br/>
<br/>
Click the button below to see more information about this session.
</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW
LESSON DETAILS</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
You successfully completed a lesson with <%= @student.name %>.
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,17 @@
<% provide(:title, "#{@student.name} has proposed a different time for their lesson") %>
<% provide(:photo_url, @student.resolved_photo_url) %>
<% content_for :note do %>
<p>
<%= @student.name %> has proposed a different time for their lesson request.
<br/>
<br/>
Click the button below to get more information and respond.
</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW
LESSON DETAILS</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
<%= @student.name %> has proposed a different time for their lesson request.
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,23 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @student.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @teacher.name %>,
</p>
<p>
Your student, <%= @student.name %>, has been billed for this month's lessons. You should receive your funds within 48 hours.
</p>
<p>
If you had technical problems during your lesson, or have questions, or would like to make suggestions
on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<br/>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,9 @@
Hello <%= @teacher.name %>,
Your student, <%= @student.name %>, has been billed for this month's lessons. You should receive your funds within 48 hours.
If you had technical problems during your lesson, or have questions, or would like to make suggestions on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
Best Regards,
Team JamKazam

View File

@ -0,0 +1,23 @@
<% provide(:title, "Your JamClass lesson today with #{@student.first_name}") %>
<% provide(:photo_url, @student.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @teacher.name %>,
</p>
<p>
Your student <%= @student.name %> will be billed for today's lesson, and you should receive payment within 48 hours.
</p>
<p>
If you had technical problems during your lesson, or have questions, or would like to make suggestions
on how to improve JamClass, please email us at <a href="mailto:support@jamkazam.com" style="color:#fc0">support@jamkazam.com</a>.
</p>
<br/>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,11 @@
Hello <%= @teacher.name %>,
Your student <%= @student.name %> will be billed for today's lesson, and you will receive payment within 24 hours.
If you had technical problems during your lesson, or have questions, or would like to make suggestions
on how to improve JamClass, please email us at support@jamkazam.com.
Best Regards,
Team JamKazam

View File

@ -0,0 +1,23 @@
<% provide(:title, "Your student #{@student.name} will not be charged for their lesson") %>
<% provide(:photo_url, @student.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @teacher.name %>,
</p>
<p>Your student <%= @student.name %> will not be billed for today's session.
<br/>
<br/>
Click the button below to see more information about this session.
</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW
LESSON DETAILS</a>
</p>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,5 @@
Hello <%= @teacher.name %>,
Your student <%= @student.name %> will not be billed for today's session.
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,11 @@
<% provide(:title, "Lesson Request from #{@sender.name}") %>
<% provide(:photo_url, @sender.resolved_photo_url) %>
<% content_for :note do %>
<p>This student has requested to schedule a <%= @lesson_booking.display_type %> lesson. <br /><br/>Click the button below to get more information and to respond to this lesson request. You must respond to this lesson request promptly, or it will be cancelled, thank you!</p>
<p>
<a href="<%= @lesson_booking.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON REQUEST</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
<%= @sender.name %> has requested a lesson.
To see this lesson request, click here: <%= @lesson_booking.home_url %>

View File

@ -0,0 +1,20 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @lesson_session.student.resolved_photo_url) %>
<% content_for :note do %>
<p>
All lessons with <%= @lesson_session.student.name %> have been rescheduled.
<% if @message.present? %>
<br/><br/><%= @sender.name %> says:
<br/><%= @message %>
<br/>
<% end %>
<br/><br/>Click the button below to get more information and to update your calendar!
<br/><br/>We strongly suggest adding this to your calendar so you don't forget it, or you'll end up paying for a lesson you don't get.</p>
<p>
<a href="<%= @lesson_session.web_url %>" style="margin: 8px 0 0 0;background-color: #ed3618;border: solid 1px #F27861;padding: 3px 10px;font-size: 12px;font-weight: 300;cursor: pointer;color: #FC9;text-decoration: none;line-height: 12px;text-align: center;">VIEW LESSON DETAILS</a>
</p>
<% end %>

View File

@ -0,0 +1,3 @@
All your lessons with <%= @lesson_session.student.name %> have been rescheduled.
To see this lesson, click here: <%= @lesson_session.web_url %>

View File

@ -0,0 +1,11 @@
<% provide(:title, @subject) %>
<p><%= @body %></p>
<p>
Session Name: <%= @session_name %><br/>
<%= @session_description %></br>
<%= @session_date %>
</p>
<p><a style="color: #ffcc00;" href="<%= @session_url %>">VIEW LESSON DETAILS</a></p>

View File

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

View File

@ -0,0 +1,21 @@
<% provide(:title, @subject) %>
<% provide(:photo_url, @student.resolved_photo_url) %>
<% content_for :note do %>
<p>
Hello <%= @teacher.name %>,
</p>
<p>
<% if @lesson_booking.is_suspended? %>
Your weekly lessons with <%= @student.name %> have been suspended because we have tried repeatedly to charge their credit card but the charge was declined. They have been asked to re-enter updated credit card info.
<% else %>
We have tried to charge the credit card of <%= @student.name %> but the charge was declined. They still have time to re-enter their credit card info before the session is suspended, though.
<% end %>
</p>
<p>
Best Regards,<br>Team JamKazam
</p>
<% end %>

View File

@ -0,0 +1,14 @@
Hello <%= @student.name %>,
<%= @subject %>
<% if @lesson_booking.is_suspended? %>
Your weekly lessons with <%= @student.name %> have been suspended because we have tried repeatedly to charge their credit card but the charge was declined. They have been asked to re-enter updated credit card info.
<% else %>
We have tried to charge the credit card of <%= @student.name %> but the charge was declined. They still have time to re-enter their credit card info before the session is suspended, though.
<% end %>
Best Regards,
Team JamKazam

View File

@ -0,0 +1,119 @@
<% provide(:title, 'Welcome to JamKazam!') %>
<% if !@user.anonymous? %>
<p>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --
</p>
<% end %>
<p> We're delighted you have decided to join the JamKazam community of musicians, and we hope
you will enjoy using JamKazam and JamTracks to play more music. Following are some
resources and some things you can do to get the most out of JamKazam.
</p>
<p><b style="color: white;">Playing With JamTracks</b><br/>
JamTracks are the best way to play along with your favorite songs. Far better and different than
traditional backing tracks, our JamTracks are complete multi-track professional recordings, with
fully isolated tracks for each part of the music. And our free app and Internet service are packed
with features that give you unmatched creative freedom to learn, practice, record, play with
others, and share your performances. Here are some great JamTracks resources:
</p>
<ul>
<li><a style="color:#fc0" href="https://www.youtube.com/watch?v=07zJC7C2ICA">JamTracks Overview Video</a> - See all the great things you can do with JamTracks.</li>
<li><a style="color:#fc0" href="https://jamkazam.desk.com/customer/en/portal/articles/2124663-playing-with-jamtracks">JamTracks User Guide</a> - A set of articles that explain how to use all the JamTracks
features.</li>
<li><a style="color:#fc0" href="https://www.jamkazam.com/client#/jamtrack">Get a JamTrack Free</a> - A web page you can visit to search our catalog of JamTracks.
When you find a song you like, click the Get It Free button, and your first one is free! If
you already redeemed a free JamTrack or purchased JamTracks, you can also access
them on this page from your web browser.</li>
<li><a style="color:#fc0" href="https://www.jamkazam.com/downloads">JamKazam Application</a> - A web page where you can download our free Mac or Windows
app. The app lets you do a lot more with JamTracks than you can do in a browser.</li>
</ul>
<p><b style="color: white;">Play Live With Others from Different Locations on JamKazam</b><br/>
JamKazams free app lets musicians play together live and in sync from different locations over
the Internet. Kind of like Skype on super audio steroids, with ultra low latency and terrific audio
quality. You can set up online sessions that are public or private, for you alone or for others to
join. You can find and join others sessions, use backing tracks and loops in sessions, make
audio and video recordings of session performances, and more. <a style="color:#fc0" href="https://jamkazam.desk.com/customer/en/portal/topics/673198-tutorials-on-major-features/articles">Click here for a set of tutorial
videos that show how to use these features</a>.
</p>
<p><b style="color: white;">Teach or Take Online Music Lessons</b><br/>
If you teach music lessons and have tried to give lessons using Skype, youll know how
unsatisfactory that experience is. Audio quality is poor, and latency prohibits teacher and
student playing together at all. JamKazam is a terrific service for teaching and taking online
music lessons. If you want to use JamKazam for lessons, well be happy to support both you and
your students in getting set up and ready to go.
</p>
<p><b style="color: white;">Complete Your Profile</b><br/>
Every member of our community has a profile. Its a great way to share a little bit about who
you are as a musician, as well as your musical interests. For example, what instruments do you
play? What musical genres do you like best? Are you interested in getting into a virtual/online
or a real-world band? And so on. Filling out your profile will help you connect with others with
common interests. To do this, go to <a style="color:#fc0" href="https://www.jamkazam.com/client">www.jamkazam.com</a> or launch the JamKazam app. Then
click on the Profile tile, and click the Edit Profile button.
</p>
<p><b style="color: white;">Invite Your Friends</b><br/>
Have friends who are musicians? Invite them to join you to play together on JamKazam. To do
this, go to <a style="color:#fc0" href="https://www.jamkazam.com/client">www.jamkazam.com</a> or launch the JamKazam app. Then move your mouse over the
user icon in the top right corner of the screen. A menu will be displayed. Click Invite Friends in
this menu, and you can then use the options to invite your friends using their email addresses,
or via Facebook, or using your Google contacts.
<p>
<p><b style="color: white;">Get Help</b><br/>
If you run into trouble and need help, please reach out to us. We will be glad to do everything
we can to answer your questions and get you up and running. You can visit our <a style="color:#fc0" href="https://jamkazam.desk.com/">Support Portal</a>
to find knowledge base articles and post questions that have not already been answered. You
can email us at <a style="color:#fc0" href="mailto:support@jamkazam.com">support@jamkazam.com</a>. And if you just want to chat, share tips and war
stories, and hang out with fellow JamKazamers, you can visit our <a style="color:#fc0" href="http://forums.jamkazam.com/">Community Forum</a>.
<p>
<p>
<br/>
<br/>
Again, welcome to JamKazam, and we hope you have a great time here!
</p>
<p>Best Regards,<br/>
Team JamKazam</p>

View File

@ -0,0 +1,43 @@
<% if !@user.anonymous? %>Hello <%= EmailBatchProgression::VAR_FIRST_NAME %> --<% end %>
We're delighted that you have decided to try the JamKazam service, and we hope that you will enjoy using JamKazam to play music with others. Following are some resources that can help you get oriented and get the most out of JamKazam.
Getting Started
---------------
There are basically three kinds of setups you can use to play on JamKazam.
* Built-In Audio on Your Computer - You can use a Windows or Mac computer, and just use the built-in mic and headphone jack to handle your audio. This is cheap and easy, but your audio quality will suffer, and it will also process audio very slowly, creating problems with latency, or lag, in your sessions. Still, you can at least start experimenting with JamKazam in this way.
* Computer with External Audio Interface - - You can use a Windows or Mac computer with an external audio interface that you already own and use for recording, if you happen to have one already. If you are going to do this, or use the built-in mic/headphones on your computer, please refer to our Minimum System Requirements at https://jamkazam.desk.com/customer/portal/articles/1288274-minimum-system-requirements to make sure your computer will work. These requirements were on the download page for the app, but you may have sped by them. Also, we'd recommend watching our Getting Started Video at https://www.youtube.com/watch?v=DBo--aj_P1w to learn more about your options here.
* The JamBlaster - JamKazam has designed a new product from the ground up to be the best way to play music online in real time. It's called the JamBlaster. It processes audio faster than any of the thousands of combinations of computers and interfaces in use on JamKazam today, which means you can play with musicians who are farther away from you, and closer sessions will feel/sound tighter. The JamBlaster is both a computer and an audio interface, so it also eliminates the system requirements worries, and it "just works" so you don't have to be an audio and computer genius to get it working. This is a great product - available only through a Kickstarter program running during a 30-day window during parts of February and March 2015. You can watch the JamBlaster Video at https://www.youtube.com/watch?v=gAJAIHMyois to learn more about this amazing new product.
JamKazam Features
-----------------
JamKazam offers a very robust and exciting set of features for playing online and sharing your performances with others. Here are some videos you can watch to easily get up to speed on some of the things you can do with JamKazam:
* Creating a Session - https://www.youtube.com/watch?v=EZZuGcDUoWk
* Finding a Session - https://www.youtube.com/watch?v=xWponSJo-GU
* Playing in a Session - https://www.youtube.com/watch?v=zJ68hA8-fLA
* Connecting with Other Musicians - https://www.youtube.com/watch?v=4KWklSZZxRc
* Working with Recordings - https://www.youtube.com/watch?v=Gn-dOqnNLoY
Getting Help
------------
If you run into trouble and need help, please reach out to us. We will be glad to do everything we can to answer your questions and get you up and running. You can visit our Support Portal at https://jamkazam.desk.com/ to find knowledge base articles and post questions that have not already been answered. You can email us at support@jamkazam.com. And if you just want to chat, share tips and war stories, and hang out with fellow JamKazamers, you can visit our Community Forum at http://forums.jamkazam.com/.
Again, welcome to JamKazam, and we look forward to seeing and hearing you online soon!
Best Regards,
Team JamKazam

View File

@ -5,13 +5,14 @@
<title>JamKazam</title>
<style>
p {
margin-bottom:0px;
line-height:140%;
}
a {
color: #ffcc00 !important;
}
p {
margin-bottom: 0px;
line-height: 140%;
}
a {
color: #ffcc00 !important;
}
</style>
</head>
@ -25,15 +26,22 @@
<table bgcolor="#262626" width="650" align="center" cellpadding="30" cellspacing="0">
<tr>
<td align="left"><h1 style="font-size:22px;font-weight:normal;margin-top:0px"><font color="#F34E1C" face="Arial, Helvetica, sans-serif"><%= yield(:title) %></font></h1>
<td align="left"><h1 style="font-size:22px;font-weight:normal;margin-top:0px">
<font color="#F34E1C" face="Arial, Helvetica, sans-serif"><%= yield(:title) %></font></h1>
<p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><%= yield %></font></p>
<br>
<table>
<tr>
<td width="58" align="left" valign="top"><p style="padding:2px;width:54px;height:54px;background-color:#ed3618;-webkit-border-radius:28px;-moz-border-radius:28px;border-radius:28px;margin-right:10px"><img src="<%= yield(:photo_url) %>" width="54" height="54" style="-webkit-border-radius:26px;-moz-border-radius:26px;border-radius:26px;"></p></td>
<td width="58" align="left" valign="top">
<p style="padding:2px;width:54px;height:54px;background-color:#ed3618;-webkit-border-radius:28px;-moz-border-radius:28px;border-radius:28px;margin-right:10px">
<img src="<%= yield(:photo_url) %>" width="54" height="54" style="-webkit-border-radius:26px;-moz-border-radius:26px;border-radius:26px;">
</p></td>
<td><p><font size="3" color="#AAAAAA" face="Arial, Helvetica, sans-serif"><%= yield(:note) %></font></p>
</td></tr></table>
</td>
</tr>
</table>
</td>
@ -43,24 +51,30 @@
<% unless @suppress_user_has_account_footer == true %>
<tr>
<td>
<table bgcolor="#21474C" cellpadding="10" cellspacing="0">
<tr>
<td align="left">
<tr>
<td>
<table bgcolor="#21474C" cellpadding="10" cellspacing="0">
<tr>
<td align="left">
<!-- CALL OUT BOX -->
</font></p>
<p style="margin-top:0px"><font size="2" color="#7FACBA" face="Arial, Helvetica, sans-serif">This email was sent to you because you have an account at <a style="color: #ffcc00;" href="https://www.jamkazam.com">JamKazam</a>.
</td></tr></table>
<!-- CALL OUT BOX -->
</font></p>
<p style="margin-top:0px">
<font size="2" color="#7FACBA" face="Arial, Helvetica, sans-serif">This email was sent to you because
you have an account at <a style="color: #ffcc00;" href="https://www.jamkazam.com">JamKazam</a>.
</td>
</tr>
</table>
</td>
</tr>
</td>
</tr>
<% end %>
</table>
<table align="center" width="650" cellpadding="10" bgcolor="#156572" cellspacing="0">
<tr>
<td align="center"><font size="1" color="#ffffff" face="Arial, Helvetica, sans-serif">Copyright &copy; <%= Time.now.year %> JamKazam, Inc. All rights reserved.</font>
<td align="center">
<font size="1" color="#ffffff" face="Arial, Helvetica, sans-serif">Copyright &copy; <%= Time.now.year %> JamKazam,
Inc. All rights reserved.</font>
</td>
</tr>
</table>

View File

@ -0,0 +1,72 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JamKazam</title>
<style>
p {
margin-bottom: 0px;
line-height: 140%;
}
a {
color: #ffcc00 !important;
}
</style>
</head>
<body bgcolor="#000000" style="margin-top:10px;font-family:Arial, Helvetica, sans-serif;">
<table bgcolor="#262626" width="650" align="center" cellpadding="0" cellspacing="0">
<tr>
<td><img src="https://www.jamkazam.com/assets/email/header.png" width="650" height="183" alt="JamKazam"></td>
</tr>
</table>
<table bgcolor="#262626" width="650" align="center" cellpadding="30" cellspacing="0">
<tr>
<td align="left"><h1 style="font-size:22px;font-weight:normal;margin-top:0px">
<font color="#F34E1C" face="Arial, Helvetica, sans-serif"><%= yield(:title) %></font></h1>
<font color="#AAAAAA" face="Arial, Helvetica, sans-serif">
<%= yield %>
</font>
</td>
</tr>
<% unless @suppress_user_has_account_footer == true %>
<tr>
<td>
<table bgcolor="#21474C" cellpadding="10" cellspacing="0">
<tr>
<td align="left">
<!-- CALL OUT BOX -->
</font></p>
<p style="margin-top:0px">
<font size="2" color="#7FACBA" face="Arial, Helvetica, sans-serif">This email was sent to you because
you have an account at <a style="color: #ffcc00;" href="https://www.jamkazam.com">JamKazam</a>.
</td>
</tr>
</table>
</td>
</tr>
<% end %>
</table>
<table align="center" width="650" cellpadding="10" bgcolor="#156572" cellspacing="0">
<tr>
<td align="center">
<font size="1" color="#ffffff" face="Arial, Helvetica, sans-serif">Copyright &copy; <%= Time.now.year %> JamKazam,
Inc. All rights reserved.</font>
</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,7 @@
<%= yield %>
<% unless @user.nil? || @suppress_user_has_account_footer == true %>
This email was sent to you because you have an account at JamKazam / https://www.jamkazam.com. To unsubscribe: https://www.jamkazam.com/unsubscribe/<%=@user.unsubscribe_token%>.
<% end %>
Copyright <%= Time.now.year %> JamKazam, Inc. All rights reserved.

View File

@ -54,4 +54,7 @@ module NotificationTypes
MIXDOWN_SIGN_COMPLETE = "MIXDOWN_SIGN_COMPLETE"
MIXDOWN_SIGN_FAILED = "MIXDOWN_SIGN_FAILED"
# jamclass
LESSON_MESSAGE = "LESSON_MESSAGE"
SCHEDULED_JAMCLASS_INVITATION = "SCHEDULED_JAMCLASS_INVITATION"
end

View File

@ -82,8 +82,32 @@ module JamRuby
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
if data.has_key?('values') || data.has_key?(:values)
@client.write_point(name, data)
data['timestamp'] = Time.now.to_i
tags = data['tags']
key = 'tags' if tags
tags ||= data[:tags]
key = :tags if key.nil?
tags ||= {}
key = :tags if key.nil?
tags['host'] = @host
data[key] = tags
else
tags = {}
values = {}
for k,v in data
if v.is_a?(String)
tags[k] = v
else
values[k] = v
end
end
data = {tags: tags, values: values}
end
@client.write_point(name, data)
end
end

View File

@ -524,21 +524,41 @@ module JamRuby
)
end
def scheduled_session_invitation(receiver_id, session_id, photo_url, msg, session_name, session_date, notification_id, created_at)
scheduled_session_invitation = Jampb::ScheduledSessionInvitation.new(
def scheduled_jamclass_invitation(receiver_id, session_id, photo_url, msg, session_name, session_date, notification_id, created_at, lesson_session_id)
scheduled_jamclas_invitation = Jampb::ScheduledJamclassInvitation.new(
:session_id => session_id,
:photo_url => photo_url,
:msg => msg,
:session_name => session_name,
:session_date => session_date,
:notification_id => notification_id,
:created_at => created_at
:created_at => created_at,
lesson_session_id: lesson_session_id
)
Jampb::ClientMessage.new(
:type => ClientMessage::Type::SCHEDULED_SESSION_INVITATION,
:type => ClientMessage::Type::SCHEDULED_JAMCLASS_INVITATION,
:route_to => USER_TARGET_PREFIX + receiver_id,
:scheduled_session_invitation => scheduled_session_invitation
:scheduled_jamclass_invitation => scheduled_jamclas_invitation
)
end
def scheduled_session_invitation(receiver_id, session_id, photo_url, msg, session_name, session_date, notification_id, created_at)
scheduled_session_invitation = Jampb::ScheduledSessionInvitation.new(
:session_id => session_id,
:photo_url => photo_url,
:msg => msg,
:session_name => session_name,
:session_date => session_date,
:notification_id => notification_id,
:created_at => created_at
)
Jampb::ClientMessage.new(
:type => ClientMessage::Type::SCHEDULED_SESSION_INVITATION,
:route_to => USER_TARGET_PREFIX + receiver_id,
:scheduled_session_invitation => scheduled_session_invitation
)
end
@ -949,6 +969,29 @@ module JamRuby
)
end
# creates the general purpose text message
def lesson_message(receiver_id, sender_photo_url, sender_name, sender_id, msg, notification_id, music_session_id, created_at, student_directed, purpose, lesson_session_id)
lesson_message = Jampb::LessonMessage.new(
:photo_url => sender_photo_url,
:sender_name => sender_name,
:sender_id => sender_id,
:receiver_id => receiver_id,
:msg => msg,
:notification_id => notification_id,
:music_session_id => music_session_id,
:created_at => created_at,
:student_directed => student_directed,
:purpose => purpose,
:lesson_session_id => lesson_session_id
)
Jampb::ClientMessage.new(
:type => ClientMessage::Type::LESSON_MESSAGE,
:route_to => USER_TARGET_PREFIX + receiver_id,
:lesson_message => lesson_message
)
end
# creates the chat message
def chat_message(session_id, sender_name, sender_id, msg, msg_id, created_at, channel)
chat_message = Jampb::ChatMessage.new(

View File

@ -0,0 +1,19 @@
module JamRuby
class AffiliateDistribution < ActiveRecord::Base
belongs_to :sale_line_item, class_name: 'JamRuby::SaleLineItem'
belongs_to :affiliate_referral, class_name: 'JamRuby::AffiliatePartner', foreign_key: :affiliate_referral_id
validates :affiliate_referral, presence:true
validates :affiliate_referral_fee_in_cents, numericality: {only_integer: false}
def self.create(affiliate_referral, fee_in_cents, sale_line_item)
distribution = AffiliateDistribution.new
distribution.affiliate_referral = affiliate_referral
distribution.affiliate_referral_fee_in_cents = fee_in_cents
distribution.sale_line_item = sale_line_item
distribution
end
end
end

View File

@ -9,6 +9,7 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
has_many :months, :class_name => 'JamRuby::AffiliateMonthlyPayment', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
has_many :traffic_totals, :class_name => 'JamRuby::AffiliateTrafficTotal', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
has_many :visits, :class_name => 'JamRuby::AffiliateReferralVisit', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
has_many :affiliate_distributions, :class_name => "JamRuby::AffiliateDistribution", foreign_key: :affiliate_referral_id
attr_accessible :partner_name, :partner_code, :partner_user_id, :entity_type, :rate, as: :admin
ENTITY_TYPES = %w{ Individual Sole\ Proprietor Limited\ Liability\ Company\ (LLC) Partnership Trust/Estate S\ Corporation C\ Corporation Other }
@ -118,17 +119,19 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
sale_time - user.created_at < 2.years
end
def should_attribute_sale?(shopping_cart)
def should_attribute_sale?(shopping_cart, user_to_check, instance)
if created_within_affiliate_window(shopping_cart.user, Time.now)
product_info = shopping_cart.product_info
if created_within_affiliate_window(user_to_check, Time.now)
product_info = shopping_cart.product_info(instance)
# subtract the total quantity from the freebie quantity, to see how much we should attribute to them
real_quantity = product_info[:quantity].to_i - product_info[:marked_for_redeem].to_i
{fee_in_cents: (product_info[:price] * 100 * real_quantity * rate.to_f).round}
applicable_rate = shopping_cart.is_lesson? ? lesson_rate : rate
{fee_in_cents: (product_info[:price] * 100 * real_quantity * applicable_rate.to_f).round}
else
false
end
end
def cumulative_earnings_in_dollars
@ -233,23 +236,23 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
def self.sale_items_subquery(start_date, end_date, table_name)
%{
FROM sale_line_items
FROM affiliate_distributions inner join sale_line_items ON affiliate_distributions.sale_line_item_id = sale_line_items.id
WHERE
(DATE(sale_line_items.created_at) >= DATE('#{start_date}') AND DATE(sale_line_items.created_at) <= DATE('#{end_date}'))
(DATE(affiliate_distributions.created_at) >= DATE('#{start_date}') AND DATE(affiliate_distributions.created_at) <= DATE('#{end_date}'))
AND
sale_line_items.affiliate_referral_id = #{table_name}.affiliate_partner_id
affiliate_distributions.affiliate_referral_id = #{table_name}.affiliate_partner_id
}
end
def self.sale_items_refunded_subquery(start_date, end_date, table_name)
%{
FROM sale_line_items
FROM affiliate_distributions inner join sale_line_items ON affiliate_distributions.sale_line_item_id = sale_line_items.id
WHERE
(DATE(sale_line_items.affiliate_refunded_at) >= DATE('#{start_date}') AND DATE(sale_line_items.affiliate_refunded_at) <= DATE('#{end_date}'))
(DATE(affiliate_distributions.affiliate_refunded_at) >= DATE('#{start_date}') AND DATE(affiliate_distributions.affiliate_refunded_at) <= DATE('#{end_date}'))
AND
sale_line_items.affiliate_referral_id = #{table_name}.affiliate_partner_id
affiliate_distributions.affiliate_referral_id = #{table_name}.affiliate_partner_id
AND
sale_line_items.affiliate_refunded = TRUE
affiliate_distributions.affiliate_refunded = TRUE
}
end
# total up quarters by looking in sale_line_items for items that are marked as having a affiliate_referral_id
@ -269,22 +272,22 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
last_updated = NOW(),
jamtracks_sold =
COALESCE(
(SELECT COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND sale_line_items.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
(SELECT COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND affiliate_distributions.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
#{sale_items_subquery(start_date, end_date, 'affiliate_monthly_payments')}
), 0)
+
COALESCE(
(SELECT -COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND sale_line_items.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
(SELECT -COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND affiliate_distributions.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_monthly_payments')}
), 0),
due_amount_in_cents =
COALESCE(
(SELECT SUM(affiliate_referral_fee_in_cents)
(SELECT SUM(affiliate_distributions.affiliate_referral_fee_in_cents)
#{sale_items_subquery(start_date, end_date, 'affiliate_monthly_payments')}
), 0)
+
COALESCE(
(SELECT -SUM(affiliate_referral_fee_in_cents)
(SELECT -SUM(affiliate_distributions.affiliate_referral_fee_in_cents)
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_monthly_payments')}
), 0)
@ -322,22 +325,22 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
last_updated = NOW(),
jamtracks_sold =
COALESCE(
(SELECT COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND sale_line_items.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
(SELECT COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND affiliate_distributions.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
#{sale_items_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
), 0)
+
COALESCE(
(SELECT -COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND sale_line_items.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
(SELECT -COUNT(CASE WHEN sale_line_items.product_type = 'JamTrack' AND affiliate_distributions.affiliate_referral_fee_in_cents > 0 THEN 1 ELSE NULL END)
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
), 0),
due_amount_in_cents =
COALESCE(
(SELECT SUM(affiliate_referral_fee_in_cents)
(SELECT SUM(affiliate_distributions.affiliate_referral_fee_in_cents)
#{sale_items_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
), 0)
+
COALESCE(
(SELECT -SUM(affiliate_referral_fee_in_cents)
(SELECT -SUM(affiliate_distributions.affiliate_referral_fee_in_cents)
#{sale_items_refunded_subquery(start_date, end_date, 'affiliate_quarterly_payments')}
), 0)

View File

@ -0,0 +1,51 @@
module JamRuby
class AffiliatePaymentCharge < Charge
has_one :teacher_payment, class_name: "JamRuby::TeacherPayment", foreign_key: :affiliate_charge_id
def distribution
@distribution ||= teacher_payment.teacher_distribution
end
def max_retries
9999999
end
def teacher
@teacher ||= teacher_payment.teacher
end
def charged_user
teacher
end
def do_charge
# source will let you supply a token. But... how to get a token in this case?
stripe_charge = Stripe::Charge.create(
:amount => amount_in_cents,
:currency => "usd",
:customer => APP_CONFIG.stripe[:source_customer],
:description => construct_description,
:destination => teacher.teacher.stripe_account_id,
:application_fee => fee_in_cents,
)
stripe_charge
end
def do_send_notices
UserMailer.teacher_distribution_done(teacher_payment)
end
def do_send_unable_charge
UserMailer.teacher_distribution_fail(teacher_payment)
end
def construct_description
teacher_payment.teacher_distribution.description
end
end
end

View File

@ -0,0 +1,134 @@
module JamRuby
class Charge < ActiveRecord::Base
validates :sent_billing_notices, inclusion: {in: [true, false]}
def max_retries
raise "not implemented"
end
def do_charge(force)
raise "not implemented"
end
def do_send_notices
raise "not implemented"
end
def do_send_unable_charge
raise "not implemented"
end
def charge_retry_hours
24
end
def charged_user
raise "not implemented"
end
def charge(force = false)
stripe_charge = nil
if !self.billed
# check if we can bill at the moment
if !force && last_billing_attempt_at && (charge_retry_hours.hours.ago < last_billing_attempt_at)
return false
end
if !force && !billing_should_retry
return false
end
# bill the user right now. if it fails, move on; will be tried again
self.billing_attempts = self.billing_attempts + 1
self.billing_should_retry = self.billing_attempts < max_retries
self.last_billing_attempt_at = Time.now
self.save(validate: false)
begin
stripe_charge = do_charge(force)
self.stripe_charge_id = stripe_charge.id
self.billed = true
self.billed_at = Time.now
self.save(validate: false)
rescue Stripe::StripeError => e
stripe_handler(e)
subject = "Unable to charge user #{charged_user.email} for lesson #{self.id} (stripe)"
body = "user=#{charged_user.email}\n\nbilling_error_reason=#{billing_error_reason}\n\nbilling_error_detail = #{billing_error_detail}"
AdminMailer.alerts({subject: subject, body: body})
do_send_unable_charge
return false
rescue Exception => e
subject = "Unable to charge user #{charged_user.email} for lesson #{self.id} (unhandled)"
body = "user=#{charged_user.email}\n\nbilling_error_reason=#{billing_error_reason}\n\nbilling_error_detail = #{billing_error_detail}"
AdminMailer.alerts({subject: subject, body: body})
unhandled_handler(e)
return false
end
end
if !self.sent_billing_notices
# If the charge is successful, then we post the charge to the students payment history,
# and associate the charge with the lesson, so that everyone knows the student has paid, and we send an email
do_send_notices
self.sent_billing_notices = true
self.sent_billing_notices_at = Time.now
self.post_processed = true
self.post_processed_at = Time.now
self.save(validate: false)
end
return stripe_charge
end
def unhandled_handler(e, reason = 'unhandled_exception')
self.billing_error_reason = reason
if e.cause
self.billing_error_detail = e.cause.to_s + "\n" + e.cause.backtrace.join("\n\t") if e.cause.backtrace
self.billing_error_detail << "\n\n"
self.billing_error_detail << e.to_s + "\n" + e.backtrace.join("\n\t") if e.backtrace
else
self.billing_error_detail = e.to_s + "\n" + e.backtrace.join("\n\t") if e.backtrace
end
self.save(validate: false)
end
def is_card_declined?
billed == false && billing_error_reason == 'card_declined'
end
def is_card_expired?
billed == false && billing_error_reason == 'card_expired'
end
def last_billed_at_date
last_billing_attempt_at.strftime("%B %d, %Y") if last_billing_attempt_at
end
def stripe_handler(e)
msg = e.to_s
if msg.include?('declined')
self.billing_error_reason = 'card_declined'
self.billing_error_detail = msg
elsif msg.include?('expired')
self.billing_error_reason = 'card_expired'
self.billing_error_detail = msg
elsif msg.include?('processing')
self.billing_error_reason = 'processing_error'
self.billing_error_detail = msg
else
self.billing_error_reason = 'stripe'
self.billing_error_detail = msg
end
self.save(validate: false)
end
end
end

View File

@ -3,18 +3,42 @@ module JamRuby
include HtmlSanitize
html_sanitize strict: [:message]
CHANNEL_LESSON = 'lesson'
self.table_name = 'chat_messages'
self.primary_key = 'id'
default_scope order('created_at DESC')
attr_accessor :ignore_message_checks
attr_accessible :user_id, :message, :music_session_id
belongs_to :user
belongs_to :music_session
belongs_to :target_user, class_name: "JamRuby::User"
belongs_to :lesson_booking, class_name: "JamRuby::LessonBooking"
validates :user, presence: true
validates :message, length: {minimum: 1, maximum: 255}, no_profanity: true
validates :message, length: {minimum: 1, maximum: 255}, no_profanity: true, unless: :ignore_message_checks
def self.create(user, music_session, message, channel, client_id, target_user = nil, lesson_booking = nil)
chat_msg = ChatMessage.new
chat_msg.user_id = user.id
chat_msg.music_session_id = music_session.id if music_session
chat_msg.message = message
chat_msg.channel = channel
chat_msg.target_user = target_user
chat_msg.lesson_booking = lesson_booking
if lesson_booking
chat_msg.ignore_message_checks = true
end
if chat_msg.save
ChatMessage.send_chat_msg music_session, chat_msg, user, client_id, channel
end
chat_msg
end
class << self
@ -36,7 +60,7 @@ module JamRuby
query = ChatMessage.where('music_session_id = ?', music_session_id)
end
query = query.offset(start).limit(limit).order('created_at DESC')
query = query.offset(start).limit(limit).order('created_at DESC').includes([:user])
if query.length == 0
[query, nil]

View File

@ -1,6 +1,7 @@
module JamRuby
class Invitation < ActiveRecord::Base
INVITATION_NOT_TEACHER_VALIDATION_ERROR = "Lessons can only sent invitations to teachers"
FRIENDSHIP_REQUIRED_VALIDATION_ERROR = "You can only invite friends"
MEMBERSHIP_REQUIRED_OF_MUSIC_SESSION = "You must be a member of the music session to send invitations on behalf of it"
JOIN_REQUEST_IS_NOT_FOR_RECEIVER_AND_MUSIC_SESSION = "You can only associate a join request with an invitation if that join request comes from the invited user and if it's for the same music session"
@ -15,7 +16,7 @@ module JamRuby
validates :receiver, :presence => true
validates :music_session, :presence => true
validate :require_sender_in_music_session, :require_are_friends_or_requested_to_join
validate :require_sender_in_music_session, :require_are_friends_or_requested_to_join_or_teacher
private
@ -25,15 +26,22 @@ module JamRuby
end
end
def require_are_friends_or_requested_to_join
def require_are_friends_or_requested_to_join_or_teacher
if !join_request.nil? && (join_request.user != receiver || join_request.music_session != music_session)
errors.add(:join_request, JOIN_REQUEST_IS_NOT_FOR_RECEIVER_AND_MUSIC_SESSION )
elsif join_request.nil?
# we only check for friendship requirement if this was not in response to a join_request
unless receiver.friends.exists? sender
errors.add(:receiver, FRIENDSHIP_REQUIRED_VALIDATION_ERROR)
if !receiver.friends.exists?(sender) && (music_session.is_lesson? && receiver != music_session.lesson_session.teacher)
if !receiver.friends.exists?(sender)
errors.add(:receiver, FRIENDSHIP_REQUIRED_VALIDATION_ERROR)
elsif (music_session.is_lesson? && receiver != music_session.lesson_session.teacher)
errors.add(:receiver, INVITATION_NOT_TEACHER_VALIDATION_ERROR)
end
end
end
end
end
end

View File

@ -338,6 +338,7 @@ module JamRuby
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')
query = query.includes([{ jam_track_tracks: :instrument }, { genres_jam_tracks: :genre }])
else
query = query.group("jam_tracks.id")
if options[:sort_by] == 'jamtrack'

View File

@ -0,0 +1,826 @@
# represenst the type of lesson package
module JamRuby
class LessonBooking < ActiveRecord::Base
include HtmlSanitize
html_sanitize strict: [:description, :cancel_message]
include ActiveModel::Dirty
@@log = Logging.logger[LessonBooking]
attr_accessor :accepting, :countering, :countered_slot, :countered_lesson
STATUS_REQUESTED = 'requested'
STATUS_CANCELED = 'canceled'
STATUS_APPROVED = 'approved'
STATUS_SUSPENDED = 'suspended'
STATUS_COUNTERED = 'countered'
STATUS_TYPES = [STATUS_REQUESTED, STATUS_CANCELED, STATUS_APPROVED, STATUS_SUSPENDED, STATUS_COUNTERED]
LESSON_TYPE_FREE = 'single-free'
LESSON_TYPE_TEST_DRIVE = 'test-drive'
LESSON_TYPE_PAID = 'paid'
LESSON_TYPES = [LESSON_TYPE_FREE, LESSON_TYPE_TEST_DRIVE, LESSON_TYPE_PAID]
PAYMENT_STYLE_ELSEWHERE = 'elsewhere'
PAYMENT_STYLE_SINGLE = 'single'
PAYMENT_STYLE_WEEKLY = 'weekly'
PAYMENT_STYLE_MONTHLY = 'monthly'
PAYMENT_STYLES = [PAYMENT_STYLE_ELSEWHERE, PAYMENT_STYLE_SINGLE, PAYMENT_STYLE_WEEKLY, PAYMENT_STYLE_MONTHLY]
belongs_to :user, class_name: "JamRuby::User"
belongs_to :teacher, class_name: "JamRuby::User"
belongs_to :accepter, class_name: "JamRuby::User"
belongs_to :canceler, class_name: "JamRuby::User"
belongs_to :default_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :default_slot_id, inverse_of: :defaulted_booking
belongs_to :counter_slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :counter_slot_id, inverse_of: :countered_booking
has_many :lesson_booking_slots, class_name: "JamRuby::LessonBookingSlot"
has_many :lesson_sessions, class_name: "JamRuby::LessonSession"
has_many :lesson_package_purchases, class_name: "JamRuby::LessonPackagePurchase"
validates :user, presence: true
validates :teacher, presence: true
validates :lesson_type, inclusion: {in: LESSON_TYPES}
validates :status, presence: true, inclusion: {in: STATUS_TYPES}
validates :recurring, inclusion: {in: [true, false]}
validates :sent_notices, inclusion: {in: [true, false]}
validates :card_presumed_ok, inclusion: {in: [true, false]}
validates :active, inclusion: {in: [true, false]}
validates :lesson_length, inclusion: {in: [30, 45, 60, 90, 120]}
validates :payment_style, inclusion: {in: PAYMENT_STYLES}
validates :booked_price, presence: true
validates :description, no_profanity: true, length: {minimum: 10, maximum: 20000}
validate :validate_user, on: :create
validate :validate_recurring
validate :validate_lesson_booking_slots
validate :validate_lesson_length
validate :validate_payment_style
validate :validate_accepted, :if => :accepting
before_save :before_save
before_validation :before_validation
after_create :after_create
around_save :around_update
scope :test_drive, -> { where(lesson_type: LESSON_TYPE_TEST_DRIVE) }
scope :active, -> { where(active: true) }
scope :approved, -> { where(status: STATUS_APPROVED) }
scope :requested, -> { where(status: STATUS_REQUESTED) }
scope :canceled, -> { where(status: STATUS_CANCELED) }
scope :suspended, -> { where(status: STATUS_SUSPENDED) }
scope :engaged, -> { where("status = '#{STATUS_APPROVED}' OR status = '#{STATUS_REQUESTED}' OR status = '#{STATUS_SUSPENDED}'") }
def before_validation
if self.booked_price.nil?
self.booked_price = compute_price
end
end
def after_create
if card_presumed_ok && !sent_notices
send_notices
end
end
def before_save
automatically_default_slot
end
def around_update
@default_slot_did_change = self.default_slot_id_changed?
yield
sync_lessons
sync_remaining_test_drives
@default_slot_did_change = nil
@accepting = nil
@countering = nil
end
# here for shopping_cart
def product_info
{price: booked_price, real_price: booked_price, total_price: booked_price}
end
# here for shopping_cart
def price
booked_price
end
def alt_slot
found = nil
lesson_booking_slots.each do |slot|
if slot.id != default_slot.id
found = slot
break
end
end
found
end
def student
user
end
def next_lesson
if recurring
session = lesson_sessions.joins(:music_session).where("scheduled_start is not null").where("scheduled_start > ?", Time.now).order(:created_at).first
LessonSession.find(session.id) if session
else
lesson_sessions[0]
end
end
def accept(lesson_session, slot, accepter)
if !is_active?
self.accepting = true
end
self.active = true
self.status = STATUS_APPROVED
self.counter_slot = nil
self.default_slot = slot
self.accepter = accepter
success = self.save
if !success
puts "unable to accept lesson booking #{errors.inspect}"
end
success
end
def counter(lesson_session, proposer, slot)
self.countering = true
self.lesson_booking_slots << slot
self.counter_slot = slot
#self.status = STATUS_COUNTERED
self.save
end
def automatically_default_slot
if is_requested?
if lesson_booking_slots.length > 0
self.default_slot = lesson_booking_slots[0]
end
end
end
def sync_remaining_test_drives
if is_test_drive? || is_single_free?
if card_presumed_ok && !user_decremented
self.user_decremented = true
self.save(validate: false)
if is_single_free?
user.remaining_free_lessons = user.remaining_free_lessons - 1
elsif is_test_drive?
user.remaining_test_drives = user.remaining_test_drives - 1
end
user.save(validate: false)
end
end
end
def create_minimum_booking_time
# trying to be too smart
#(Time.now + APP_CONFIG.minimum_lesson_booking_hrs * 60*60)
Time.now
end
def sync_lessons
if is_canceled?
# don't create new sessions if cancelled
return
end
if @default_slot_did_change
end
# Here we go; let's create a lesson(s) as needed
# we need to make lessons into the future a bit, to give time for everyone involved
minimum_start_time = create_minimum_booking_time
# get all sessions that are already scheduled for this booking ahead of the minimum time
sessions = MusicSession.joins(:lesson_session).where("lesson_sessions.lesson_booking_id = ?", id).where("scheduled_start is not null").where("scheduled_start > ?", minimum_start_time).order(:created_at)
if @default_slot_did_change
# # adjust all session times
offset = 0
sessions.each_with_index do |item, i|
item.lesson_session.slot = default_slot
result = item.lesson_session.update_next_available_time(offset)
if result
offset = result
offset += 1
end
end
end
needed_sessions = determine_needed_sessions(sessions)
# if the latest scheduled session is after the minimum start time, then bump up minimum start time
last_session = sessions.last
last_session.reload if last_session # because of @default_slot_did_change logic above, this can be necessary
if last_session && last_session.scheduled_start && last_session.scheduled_start > minimum_start_time
minimum_start_time = last_session.scheduled_start
end
times = default_slot.scheduled_times(needed_sessions, minimum_start_time)
scheduled_lessons(times)
end
# sensitive to current time
def predicted_times_for_month(year, month)
first_day = Date.new(year, month, 1)
last_day = Date.new(year, month, -1)
sessions = MusicSession.joins(:lesson_session).where("lesson_sessions.lesson_booking_id = ?", id).where("scheduled_start >= ?", first_day).where("scheduled_start <= ?", last_day).order(:created_at)
times = []
sessions.each do |session|
times << session.scheduled_start
end
last_session = sessions.last
start_day = first_day
if last_session
start_day = last_session.scheduled_start.to_date + 1
end
# now flesh out the rest of the month with predicted times
more_times = default_slot.scheduled_times(5, start_day)
more_times.each do |time|
if time.to_date >= first_day && time.to_date <= last_day
times << time
end
end
times
end
def determine_needed_sessions(sessions)
needed_sessions = 0
if is_requested?
# in the case of a requested booking (not approved) only make one, even if it's recurring. This is for UI considerations
if sessions.count == 0
needed_sessions = 1
end
elsif is_active?
expected_num_sessions = recurring ? 2 : 1
needed_sessions = expected_num_sessions - sessions.count
end
needed_sessions
end
def scheduled_lessons(times)
times.each do |time|
lesson_session = LessonSession.create(self)
if lesson_session.errors.any?
puts "JamClass lesson session creation errors #{lesson_session.errors.inspect}"
@@log.error("JamClass lesson session creation errors #{lesson_session.errors.inspect}")
raise ActiveRecord::Rollback
end
ms_tz = ActiveSupport::TimeZone.new(default_slot.timezone)
ms_tz = "#{ms_tz.name},#{default_slot.timezone}"
rsvps = [{instrument_id: 'other', proficiency_level: 0, approve: true}]
music_session = MusicSession.create(student, {
name: "#{display_type2} JamClass taught by #{teacher.name}",
description: "This is a #{lesson_length}-minute #{display_type2} lesson with #{teacher.name}.",
musician_access: false,
fan_access: false,
genres: ['other'],
approval_required: false,
fan_chat: false,
legal_policy: "standard",
language: 'eng',
duration: lesson_length,
recurring_mode: false,
timezone: ms_tz,
create_type: MusicSession::CREATE_TYPE_LESSON,
is_unstructured_rsvp: true,
scheduled_start: time,
invitations: [teacher.id],
lesson_session: lesson_session,
rsvp_slots: rsvps
})
if music_session.errors.any?
puts "JamClass lesson scheduling errors #{music_session.errors.inspect}"
@@log.error("JamClass lesson scheduling errors #{music_session.errors.inspect}")
raise ActiveRecord::Rollback
end
if lesson_session.is_active?
# send out email to student to act as something they can add to their calendar
Notification.send_student_jamclass_invitation(music_session, student)
end
end
end
def is_weekly_payment?
payment_style == PAYMENT_STYLE_WEEKLY
end
def is_monthly_payment?
payment_style == PAYMENT_STYLE_MONTHLY
end
def requires_per_session_billing?
is_normal? && !is_monthly_payment?
end
def requires_teacher_distribution?(target)
if target.is_a?(JamRuby::LessonSession)
is_test_drive? || (is_normal? && !is_monthly_payment?)
elsif target.is_a?(JamRuby::LessonPackagePurchase)
is_monthly_payment?
else
raise "unable to determine object type of #{target}"
end
end
def is_requested?
status == STATUS_REQUESTED
end
def is_canceled?
status == STATUS_CANCELED
end
def is_approved?
status == STATUS_APPROVED
end
def is_suspended?
status == STATUS_SUSPENDED
end
def is_active?
active
end
def validate_accepted
# accept is multipe purpose; either accept the initial request, or a counter slot
if self.status_was != STATUS_REQUESTED && counter_slot.nil? # && self.status_was != STATUS_COUNTERED
self.errors.add(:status, "This lesson is already #{self.status}.")
end
self.accepting = false
end
def send_notices
UserMailer.student_lesson_request(self).deliver
UserMailer.teacher_lesson_request(self).deliver
Notification.send_lesson_message('requested', lesson_sessions[0], false) # TODO: this isn't quite an 'accept'
self.sent_notices = true
self.save
end
def lesson_package_type
if is_single_free?
LessonPackageType.single_free
elsif is_test_drive?
LessonPackageType.test_drive
elsif is_normal?
LessonPackageType.single
end
end
def display_type2
if is_single_free?
"Free"
elsif is_test_drive?
"TestDrive"
elsif is_normal?
"Single"
end
end
def display_type
if is_single_free?
"Free"
elsif is_test_drive?
"TestDrive"
elsif is_normal?
if recurring
"recurring"
else
"single"
end
end
end
# determine the price of this booking based on what the user wants, and the teacher's pricing
def compute_price
if is_single_free?
0
elsif is_test_drive?
LessonPackageType.test_drive.price
elsif is_normal?
teacher.teacher.booking_price(lesson_length, payment_style != PAYMENT_STYLE_MONTHLY)
end
end
def distribution_price_in_cents
if is_single_free?
0
elsif is_test_drive?
10 * 100
elsif is_normal?
booked_price * 100
end
end
def is_single_free?
lesson_type == LESSON_TYPE_FREE
end
def is_test_drive?
lesson_type == LESSON_TYPE_TEST_DRIVE
end
def is_normal?
lesson_type == LESSON_TYPE_PAID
end
def dayWeekDesc(slot = default_slot)
day = case slot.day_of_week
when 0 then "Sunday"
when 1 then "Monday"
when 2 then "Tuesday"
when 3 then "Wednesday"
when 4 then "Thursday"
when 5 then "Friday"
when 6 then "Saturday"
end
if slot.hour > 11
hour = slot.hour - 12
if hour == 0
hour = 12
end
am_pm = 'pm'
else
hour = slot.hour
if hour == 0
hour = 12
end
am_pm = 'am'
end
"#{day} at #{hour}:#{slot.minute}#{am_pm}"
end
def approved_before?
!self.accepter_id.nil?
end
def cancel(canceler, other, message)
self.active = false
self.status = STATUS_CANCELED
self.cancel_message = message
self.canceler = canceler
success = save
if success
if approved_before?
# just tell both people it's cancelled, to act as confirmation
Notification.send_lesson_message('canceled', next_lesson, false)
Notification.send_lesson_message('canceled', next_lesson, true)
UserMailer.student_lesson_booking_canceled(self, message).deliver
UserMailer.teacher_lesson_booking_canceled(self, message).deliver
chat_message_prefix = "Lesson Canceled"
else
if canceler == student
# if it's the first time acceptance student canceling, we call it a 'cancel'
Notification.send_lesson_message('canceled', next_lesson, false)
UserMailer.teacher_lesson_booking_canceled(self, message).deliver
chat_message_prefix = "Lesson Canceled"
else
# if it's the first time acceptance teacher, it was declined
UserMailer.student_lesson_booking_declined(self, message).deliver
Notification.send_lesson_message('declined', next_lesson, true)
chat_message_prefix = "Lesson Declined"
end
end
chat_message = message.nil? ? chat_message_prefix : "#{chat_message_prefix}: #{message}"
msg = ChatMessage.create(canceler, nil, chat_message, ChatMessage::CHANNEL_LESSON, nil, other, self)
end
success
end
def card_approved
self.card_presumed_ok = true
if self.save && !sent_notices
send_notices
end
end
def validate_user
if card_presumed_ok && is_single_free?
if !user.has_free_lessons?
errors.add(:user, 'have no remaining free lessons')
end
#if !user.has_stored_credit_card?
# errors.add(:user, 'has no credit card stored')
#end
elsif is_test_drive?
if user.has_requested_test_drive?(teacher) && !user.admin
errors.add(:user, "has a requested TestDrive with this teacher")
end
if !user.has_test_drives? && !user.can_buy_test_drive?
errors.add(:user, "have no remaining test drives")
end
elsif is_normal?
#if !user.has_stored_credit_card?
# errors.add(:user, 'has no credit card stored')
#end
end
end
def validate_teacher
# shouldn't we check if the teacher already has a booking in this time slot, or at least warn the user
end
def validate_recurring
if is_single_free? || is_test_drive?
if recurring
errors.add(:recurring, "can not be true for this type of lesson")
end
end
false
end
def validate_lesson_booking_slots
if lesson_booking_slots.length == 0 || lesson_booking_slots.length == 1
errors.add(:lesson_booking_slots, "must have two times specified")
end
end
def validate_lesson_length
if is_single_free? || is_test_drive?
if lesson_length != 30
errors.add(:lesson_length, "must be 30 minutes")
end
end
end
def validate_payment_style
if is_normal?
if payment_style.nil?
errors.add(:payment_style, "can't be blank")
end
end
end
def self.book_free(user, teacher, lesson_booking_slots, description)
self.book(user, teacher, LessonBooking::LESSON_TYPE_FREE, lesson_booking_slots, false, 30, PAYMENT_STYLE_ELSEWHERE, description)
end
def self.book_test_drive(user, teacher, lesson_booking_slots, description)
self.book(user, teacher, LessonBooking::LESSON_TYPE_TEST_DRIVE, lesson_booking_slots, false, 30, PAYMENT_STYLE_ELSEWHERE, description)
end
def self.book_normal(user, teacher, lesson_booking_slots, description, recurring, payment_style, lesson_length)
self.book(user, teacher, LessonBooking::LESSON_TYPE_PAID, lesson_booking_slots, recurring, lesson_length, payment_style, description)
end
def self.book(user, teacher, lesson_type, lesson_booking_slots, recurring, lesson_length, payment_style, description)
lesson_booking = nil
LessonBooking.transaction do
lesson_booking = LessonBooking.new
lesson_booking.user = user
lesson_booking.card_presumed_ok = user.has_stored_credit_card?
lesson_booking.sent_notices = false
lesson_booking.teacher = teacher
lesson_booking.lesson_type = lesson_type
lesson_booking.recurring = recurring
lesson_booking.lesson_length = lesson_length
lesson_booking.payment_style = payment_style
lesson_booking.description = description
lesson_booking.status = STATUS_REQUESTED
# two-way association slots, for before_validation loic in slot to work
lesson_booking.lesson_booking_slots = lesson_booking_slots
lesson_booking_slots.each do |slot|
slot.lesson_booking = lesson_booking
slot.message = description
end if lesson_booking_slots
if lesson_booking.save
msg = ChatMessage.create(user, lesson_booking.lesson_sessions[0], description, ChatMessage::CHANNEL_LESSON, nil, teacher, lesson_booking)
end
end
lesson_booking
end
def self.unprocessed(current_user)
LessonBooking.where(user_id: current_user.id).where(card_presumed_ok: false)
end
def self.requested(current_user)
LessonBooking.where(user_id: current_user.id).where(status: STATUS_REQUESTED)
end
def self.find_bookings_needing_sessions(minimum_start_time)
MusicSession.select([:lesson_booking_id]).joins(:lesson_session => :lesson_booking).where("lesson_bookings.active = true").where('lesson_bookings.recurring = true').where("scheduled_start is not null").where("scheduled_start > ?", minimum_start_time).group(:lesson_booking_id).having('count(lesson_booking_id) < 2')
end
# check for any recurring sessions where there are not at least 2 sessions into the future. If not, we need to make sure they get made
def self.hourly_check
schedule_upcoming_lessons
bill_monthlies
end
def self.bill_monthlies
now = Time.now
billable_monthlies(now).each do |lesson_booking|
lesson_booking.bill_monthly(now)
end
today = now.to_date
seven_days_in_future = today + 7
is_different_month = seven_days_in_future.month != today.month
if is_different_month
next_month = seven_days_in_future.to_time
billable_monthlies(next_month).each do |lesson_booking|
lesson_booking.bill_monthly(next_month)
end
end
end
def self.billable_monthlies(now)
current_month_first_day = Date.new(now.year, now.month, 1)
current_month_last_day = Date.new(now.year, now.month, -1)
#next_month_last_day = now.month == 12 ? Date.new(now.year + 1, 1, -1) : Date.new(now.year, now.month + 1, -1)
LessonBooking
.joins(:lesson_sessions => :music_session)
.joins("LEFT JOIN lesson_package_purchases ON (lesson_package_purchases.lesson_booking_id = lesson_bookings.id AND (lesson_package_purchases.year = #{current_month_first_day.year} AND lesson_package_purchases.month = #{current_month_first_day.month}))")
.where("lesson_package_purchases.id IS NULL OR (lesson_package_purchases.id IS NOT NULL AND lesson_package_purchases.post_processed = false)")
.where(payment_style: PAYMENT_STYLE_MONTHLY)
.active
.where('music_sessions.scheduled_start >= ?', current_month_first_day)
.where('music_sessions.scheduled_start <= ?', current_month_last_day).uniq
=begin
today = now.to_date
seven_days_in_future = today + 7
is_different_month = seven_days_in_future.month != today.month
if is_different_month
condition = "(((lesson_package_purchases.year = #{current_month_first_day.year} AND lesson_package_purchases.month = #{current_month_first_day.month}) AND ( (EXTRACT(YEAR FROM lesson_sessions.created_at)) = #{current_month_first_day.year} AND (EXTRACT(MONTH FROM lesson_sessions.created_at)) = #{current_month_first_day.month} ) )
OR ((lesson_package_purchases.year = #{seven_days_in_future.year} AND lesson_package_purchases.month = #{seven_days_in_future.month}) AND ( (EXTRACT(YEAR FROM lesson_sessions.created_at)) = #{seven_days_in_future.year} AND (EXTRACT(MONTH FROM lesson_sessions.created_at)) = #{seven_days_in_future.month} ) ) )"
else
condition = "((lesson_package_purchases.year = #{current_month_first_day.year} AND lesson_package_purchases.month = #{current_month_first_day.month}) AND ( (EXTRACT(YEAR FROM lesson_sessions.created_at)) = #{current_month_first_day.year} AND (EXTRACT(MONTH FROM lesson_sessions.created_at)) = #{current_month_first_day.month} ) )"
end
# .where("(lesson_package_purchases.year = #{current_month_first_day.year} AND lesson_package_purchases.month = #{current_month_first_day.month}) OR (lesson_package_purchases.year = #{next_month_last_day.year} AND lesson_package_purchases.month = #{next_month_last_day.month})")
# find any monthly-billed bookings that have a session coming up within 7 days, and if so, attempt to bill them
LessonBooking
.joins(:lesson_sessions)
.joins("LEFT JOIN lesson_package_purchases ON (lesson_package_purchases.lesson_booking_id = lesson_bookings.id AND #{condition})")
.where("lesson_package_purchases.id IS NULL OR (lesson_package_purchases.id IS NOT NULL AND lesson_package_purchases.post_processed = false)")
.where(payment_style: PAYMENT_STYLE_MONTHLY)
.where(status: STATUS_APPROVED)
.where('lesson_sessions.created_at >= ?', current_month_first_day)
.where('lesson_sessions.created_at <= ?', seven_days_in_future).uniq
=end
end
def self.bookings(student, teacher, since_at = nil)
bookings = LessonBooking.where(user_id: student.id, teacher_id: teacher.id)
if since_at
bookings = bookings.where('created_at >= ?', since_at)
end
bookings
end
def self.engaged_bookings(student, teacher, since_at = nil)
bookings = bookings(student, teacher, since_at)
bookings.engaged
end
def bill_monthly(now)
LessonBooking.transaction do
self.lock!
current_month = Date.new(now.year, now.month, 1)
bill_for_month(current_month)
today = now.to_date
seven_days_in_future = today + 7
is_different_month = seven_days_in_future.month != today.month
if is_different_month
bill_for_month(seven_days_in_future)
end
end
end
def bill_for_month(day_in_month)
# try to find lesson package purchase for this month, and last month, and see if they need processing
current_month_purchase = lesson_package_purchases.where(lesson_booking_id: self.id, user_id: student.id, year: day_in_month.year, month: day_in_month.month).first
if current_month_purchase.nil?
current_month_purchase = LessonPackagePurchase.create(user, self, lesson_package_type, day_in_month.year, day_in_month.month)
end
current_month_purchase.bill_monthly
end
def suspend!
# when this is called, the calling code sends out a email to let the student and teacher know (it feels unnatural it's not here, though)
self.status = STATUS_SUSPENDED
self.active = false
if self.save
future_sessions.each do |lesson_session|
LessonSession.find(lesson_session.id).suspend!
end
end
end
def unsuspend!
if self.status == STATUS_SUSPENDED
self.status = STATUS_APPROVED
self.active = true
if self.save
future_sessions.each do |lesson_session|
LessonSession.find(lesson_session.id).unsuspend!
end
end
end
end
def future_sessions
lesson_sessions.joins(:music_session).where('scheduled_start > ?', Time.now).order(:created_at)
end
def self.schedule_upcoming_lessons
minimum_start_time = (Time.now + APP_CONFIG.minimum_lesson_booking_hrs * 60*60)
lesson_bookings = find_bookings_needing_sessions(minimum_start_time)
lesson_bookings.each do |data|
lesson_booking = LessonBooking.find(data["lesson_booking_id"])
lesson_booking.sync_lessons
end
end
def home_url
APP_CONFIG.external_root_url + "/client#/jamclass"
end
def web_url
APP_CONFIG.external_root_url + "/client#/jamclass/lesson-booking/" + id
end
def update_payment_url
APP_CONFIG.external_root_url + "/client#/jamclass/update-payment"
end
def admin_url
APP_CONFIG.admin_root_url + "/admin/lesson_bookings/" + id
end
end
end

View File

@ -0,0 +1,239 @@
# represenst the type of lesson package
module JamRuby
class LessonBookingSlot < ActiveRecord::Base
include HtmlSanitize
html_sanitize strict: [:message, :accept_message, :cancel_message]
@@log = Logging.logger[LessonBookingSlot]
belongs_to :lesson_booking, class_name: "JamRuby::LessonBooking"
belongs_to :lesson_session, class_name: "JamRuby::LessonSession"
belongs_to :proposer, class_name: "JamRuby::User"
has_one :defaulted_booking, class_name: "JamRuby::LessonBooking", foreign_key: :default_slot_id, inverse_of: :default_slot
has_one :countered_booking, class_name: "JamRuby::LessonBooking", foreign_key: :counter_slot_id, inverse_of: :counter_slot
has_one :countered_lesson, class_name: "JamRuby::LessonSession", foreign_key: :counter_slot_id, inverse_of: :counter_slot
SLOT_TYPE_SINGLE = 'single'
SLOT_TYPE_RECURRING = 'recurring'
SLOT_TYPES = [SLOT_TYPE_SINGLE, SLOT_TYPE_RECURRING]
validates :proposer, presence: true
validates :slot_type, inclusion: {in: SLOT_TYPES}
#validates :preferred_day
validates :day_of_week, numericality: {only_integer: true}, allow_blank: true # 0 = sunday - 6 = saturday
validates :hour, numericality: {only_integer: true}
validates :minute, numericality: {only_integer: true}
validates :timezone, presence: true # example: 'America/New_York'
validates :update_all, inclusion: {in: [true, false]}
validate :validate_slot_type
validate :validate_slot_minimum_time, on: :create
validate :validate_proposer
before_validation :before_validation
def before_validation
if proposer.nil?
self.proposer = container.student
end
end
def container
if lesson_booking
lesson_booking
else
lesson_session
end
end
def is_teacher_created?
self.proposer == container.teacher
end
def is_student_created?
!is_teacher_created?
end
def is_teacher_approved?
!is_teacher_created?
end
def recipient
if is_teacher_created?
container.student
else
container.teacher
end
end
def create_minimum_booking_time
(Time.now + APP_CONFIG.minimum_lesson_booking_hrs * 60 * 60)
end
def scheduled_times(needed_sessions, minimum_start_time)
times = []
week_offset = 0
needed_sessions.times do |i|
candidate = scheduled_time(i + week_offset)
if day_of_week && candidate <= minimum_start_time
# move it up a week
week_offset += 1
candidate = scheduled_time(i + week_offset)
# sanity check
if candidate <= minimum_start_time
week_offset += 1
candidate = scheduled_time(i + week_offset)
if candidate <= minimum_start_time
raise "candidate time less than minimum start time even after scoot: #{lesson_booking.id} #{self.id}"
end
end
end
times << candidate
end
times
end
def next_day
date = Date.today
date += ((day_of_week - date.wday) % 7).abs
end
# weeks is the number of weeks in the future to compute the time for
def scheduled_time(weeks)
# get the timezone of the slot, so we can compute times
tz = TZInfo::Timezone.get(timezone)
if preferred_day
time = tz.local_to_utc(Time.new(preferred_day.year, preferred_day.month, preferred_day.day, hour, minute, 0))
else
adjusted = next_day + (weeks * 7)
# day of the week adjustment
time = tz.local_to_utc(Time.new(adjusted.year, adjusted.month, adjusted.day, hour, minute, 0))
end
time
end
def lesson_length
safe_lesson_booking.lesson_length
end
def safe_lesson_booking
found = lesson_booking
found ||= lesson_session.lesson_booking
end
def pretty_scheduled_start(with_timezone = true)
start_time = scheduled_time(0)
begin
tz = TZInfo::Timezone.get(timezone)
rescue Exception => e
@@log.error("unable to find timezone=#{tz_identifier}, e=#{e}")
end
if tz
begin
start_time = tz.utc_to_local(start_time)
rescue Exception => e
@@log.error("unable to convert #{scheduled_start} to #{tz}, e=#{e}")
puts "unable to convert #{e}"
end
end
duration = lesson_length * 60 # convert from minutes to seconds
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} #{tz}"
else
"#{start_time.strftime("%A, %B %e")} - #{start_time.strftime("%l:%M%P").strip}"
end
end
def pretty_start_time(with_timezone = true)
start_time = scheduled_time(0)
begin
tz = TZInfo::Timezone.get(timezone)
rescue Exception => e
@@log.error("unable to find timezone=#{tz_identifier}, e=#{e}")
end
if tz
begin
start_time = tz.utc_to_local(start_time)
rescue Exception => e
@@log.error("unable to convert #{scheduled_start} to #{tz}, e=#{e}")
puts "unable to convert #{e}"
end
end
duration = lesson_length * 60 # convert from minutes to seconds
end_time = start_time + duration
if with_timezone
"#{start_time.strftime("%a, %b %e")} at #{start_time.strftime("%l:%M%P").strip} #{tz.name}"
else
"#{start_time.strftime("%a, %b %e")} at #{start_time.strftime("%l:%M%P").strip}"
end
end
def validate_proposer
if proposer && (proposer != container.student && proposer != container.teacher)
errors.add(:proposer, "must be either the student or teacher")
end
end
def validate_slot_type
if slot_type == SLOT_TYPE_SINGLE
if preferred_day.nil?
errors.add(:preferred_day, "must be specified")
end
end
if slot_type == SLOT_TYPE_RECURRING
if day_of_week.nil?
errors.add(:day_of_week, "must be specified")
end
end
end
def validate_slot_minimum_time
# this code will fail miserably if the slot is malformed
if errors.any?
return
end
if is_teacher_created?
return # the thinking is that a teacher can propose much tighter to the time; since they only counter; maybe they talked to the student
end
#
# minimum_start_time = create_minimum_booking_time
minimum_start_time = Time.now
if day_of_week
# this is recurring; it will sort itself out
else
time = scheduled_time(0)
if time <= minimum_start_time
#errors.add(:base, "must be at least #{APP_CONFIG.minimum_lesson_booking_hrs} hours in the future")
errors.add(:preferred_day, "can not be in the past")
end
end
end
end
end

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