diff --git a/db/up/lessons.sql b/db/up/lessons.sql
index 974ee5272..26caee352 100644
--- a/db/up/lessons.sql
+++ b/db/up/lessons.sql
@@ -60,6 +60,8 @@ CREATE TABLE lesson_sessions (
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);
@@ -68,19 +70,22 @@ INSERT INTO lesson_package_types (id, name, description, package_type, price) VA
CREATE TABLE lesson_booking_slots (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
- lesson_booking_id VARCHAR(64) REFERENCES lesson_bookings(id) NOT NULL,
+ 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,
+ 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_sessions ADD COLUMN slot 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);
diff --git a/pb/src/client_container.proto b/pb/src/client_container.proto
index 9adacdae5..5652bfde7 100644
--- a/pb/src/client_container.proto
+++ b/pb/src/client_container.proto
@@ -652,13 +652,13 @@ message MixdownSignFailed {
}
message LessonMessage {
- optional string lesson_session_id = 1;
+ 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 received_id = 7;
+ optional string receiver_id = 7;
optional bool student_directed = 8;
optional string purpose = 9;
optional string sender_name = 10;
diff --git a/ruby/Gemfile b/ruby/Gemfile
index a4135cd06..98fe9da76 100644
--- a/ruby/Gemfile
+++ b/ruby/Gemfile
@@ -55,6 +55,7 @@ gem 'sendgrid_toolkit', '>= 1.1.1'
gem 'stripe'
gem 'zip-codes'
gem 'icalendar'
+gem 'timespan'
group :test do
gem 'simplecov', '~> 0.7.1'
diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb
index 1a0468f53..6d73ad030 100755
--- a/ruby/lib/jam_ruby.rb
+++ b/ruby/lib/jam_ruby.rb
@@ -278,6 +278,7 @@ 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"
include Jampb
diff --git a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb
index fba85bdba..e0454212a 100644
--- a/ruby/lib/jam_ruby/app/mailers/user_mailer.rb
+++ b/ruby/lib/jam_ruby/app/mailers/user_mailer.rb
@@ -751,5 +751,54 @@
format.html
end
end
+
+ # teacher proposed counter time; so send msg to the student
+ def student_lesson_counter(lesson_session, slot)
+
+ email = lesson_session.student.email
+ subject = "Instructor has proposed a different time for your lesson"
+ unique_args = {:type => "student_lesson_counter"}
+ @student = lesson_session.student
+ @teacher = lesson_session.teacher
+ @session_name = lesson_session.music_session.name
+ @session_description = lesson_session.music_session.description
+ @session_date = slot.pretty_scheduled_start(true)
+ @session_url = lesson_session.web_url
+ @lesson_session = lesson_session
+ sendgrid_category "Notification"
+ sendgrid_unique_args :type => unique_args[:type]
+
+ sendgrid_recipients([email])
+ sendgrid_substitute('@USERID', [@student.id])
+
+ mail(:to => email, :subject => subject) do |format|
+ format.text
+ format.html
+ end
+ end
+ # student proposed counter time; so send msg to the teacher
+ def teacher_lesson_counter(lesson_session, slot)
+
+ email = lesson_session.teacher.email
+ subject = "Student has proposed a different time for their lesson"
+ unique_args = {:type => "teacher_lesson_counter"}
+ @student = lesson_session.student
+ @teacher = lesson_session.teacher
+ @session_name = lesson_session.music_session.name
+ @session_description = lesson_session.music_session.description
+ @session_date = slot.pretty_scheduled_start(true)
+ @session_url = lesson_session.web_url
+ @lesson_session = lesson_session
+ sendgrid_category "Notification"
+ sendgrid_unique_args :type => unique_args[:type]
+
+ sendgrid_recipients([email])
+ sendgrid_substitute('@USERID', [@teacher.id])
+
+ mail(:to => email, :subject => subject) do |format|
+ format.text
+ format.html
+ end
+ end
end
end
diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/student_lesson_counter.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/student_lesson_counter.html.erb
new file mode 100644
index 000000000..38ec8a404
--- /dev/null
+++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/student_lesson_counter.html.erb
@@ -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 %>
+
+ <%= @teacher.name %> has proposed a different time for your lesson request.
+
+
+ Click the button below to get more information and respond.
+
+
+ VIEW
+ LESSON DETAILS
+
+<% end %>
+
+
diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/student_lesson_counter.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/student_lesson_counter.text.erb
new file mode 100644
index 000000000..67972c906
--- /dev/null
+++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/student_lesson_counter.text.erb
@@ -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 %>
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_lesson_counter.html.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_lesson_counter.html.erb
new file mode 100644
index 000000000..765f13b99
--- /dev/null
+++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_lesson_counter.html.erb
@@ -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 %>
+
+ <%= @student.name %> has proposed a different time for their lesson request.
+
+
+ Click the button below to get more information and respond.
+
+
+ VIEW
+ LESSON DETAILS
+
+<% end %>
+
+
diff --git a/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_lesson_counter.text.erb b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_lesson_counter.text.erb
new file mode 100644
index 000000000..050917a31
--- /dev/null
+++ b/ruby/lib/jam_ruby/app/views/jam_ruby/user_mailer/teacher_lesson_counter.text.erb
@@ -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 %>
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/message_factory.rb b/ruby/lib/jam_ruby/message_factory.rb
index fbb591efa..613a7733d 100644
--- a/ruby/lib/jam_ruby/message_factory.rb
+++ b/ruby/lib/jam_ruby/message_factory.rb
@@ -917,7 +917,7 @@ module JamRuby
end
# creates the general purpose text message
- def lesson_message(receiver_id, sender_photo_url, sender_name, sender_id, msg, notification_id, lesson_session_id, created_at)
+ def lesson_message(receiver_id, sender_photo_url, sender_name, sender_id, msg, notification_id, music_session_id, created_at, student_directed, purpose)
lesson_message = Jampb::LessonMessage.new(
:photo_url => sender_photo_url,
:sender_name => sender_name,
@@ -925,8 +925,10 @@ module JamRuby
:receiver_id => receiver_id,
:msg => msg,
:notification_id => notification_id,
- :lesson_session_id => lesson_session_id,
- :created_at => created_at
+ :music_session_id => music_session_id,
+ :created_at => created_at,
+ :student_directed => student_directed,
+ :purpose => purpose
)
Jampb::ClientMessage.new(
diff --git a/ruby/lib/jam_ruby/models/lesson_booking.rb b/ruby/lib/jam_ruby/models/lesson_booking.rb
index 3ba3b02cd..595cc0478 100644
--- a/ruby/lib/jam_ruby/models/lesson_booking.rb
+++ b/ruby/lib/jam_ruby/models/lesson_booking.rb
@@ -4,7 +4,7 @@ module JamRuby
@@log = Logging.logger[LessonBooking]
- attr_accessor :accepting
+ attr_accessor :accepting, :countering, :countered_slot, :countered_lesson
STATUS_REQUESTED = 'requested'
STATUS_CANCELED = 'canceled'
@@ -51,6 +51,7 @@ module JamRuby
validate :validate_payment_style
validate :validate_accepted, :if => :accepting
+
before_save :before_save
before_validation :before_validation
after_create :after_create
@@ -70,23 +71,31 @@ module JamRuby
def before_save
automatically_default_slot
- sync_remaining_test_drives
end
def after_save
sync_lessons
+ sync_remaining_test_drives
end
def student
user
end
- def accept(lesson_session, slot, update_all)
+ def accept(lesson_session, slot)
+ self.accepting = true
+ self.default_slot = slot
- if self.default_slot.nil? || update_all
- self.accepting = true
- self.default_slot = slot
- self.save!
+ if !self.save
+ raise ActiveRecord::Rollback
+ end
+ end
+
+ def counter(lesson_session, proposer, slot)
+ self.countering = true
+ self.lesson_booking_slots << slot
+ if !self.save
+ raise ActiveRecord::Rollback
end
end
@@ -100,7 +109,6 @@ module JamRuby
def sync_remaining_test_drives
if is_test_drive? || is_single_free?
- puts "CARD PRESUMED OK card_presumed_ok: #{card_presumed_ok}, user_decremented: #{user_decremented}"
if card_presumed_ok && !user_decremented
self.user_decremented = true
@@ -110,7 +118,6 @@ module JamRuby
user.remaining_free_lessons = remaining_free_lessons
elsif is_test_drive?
- puts "TEST DRIVE DECREMENT"
remaining_test_drives = user.remaining_test_drives - 1
User.where(id: user.id).update_all(remaining_test_drives: remaining_test_drives)
user.remaining_test_drives = remaining_test_drives
@@ -120,6 +127,10 @@ module JamRuby
end
end
+ def create_minimum_booking_time
+ (Time.now + APP_CONFIG.minimum_lesson_booking_hrs * 60*60)
+ end
+
def sync_lessons
if is_canceled?
@@ -129,14 +140,20 @@ module JamRuby
if !recurring && lesson_sessions.count == 1
- # if not recurring, and there is already one lession created, we are good
+ # if not recurring, and there is already one lesson created, then at most we may have to deal with a changed slot
+ if default_slot_changed?
+ if !lesson_sessions[0].update_scheduled_start(0)
+ puts "unable to update scheduled lesson"
+ raise ActiveRecord::Rollback
+ end
+ end
return
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 = (Time.now + APP_CONFIG.minimum_lesson_booking_hrs * 60*60)
+ 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)
@@ -225,11 +242,11 @@ module JamRuby
def send_notices
UserMailer.student_lesson_request(self).deliver
UserMailer.teacher_lesson_request(self).deliver
- LessonBooking.where(id: id).update_all(sent_notices: true)
+ self.sent_notices = true
+ self.save
end
def lesson_package_type
-
if is_single_free?
LessonPackageType.single_free
elsif is_test_drive?
@@ -371,15 +388,20 @@ module JamRuby
lesson_booking.sent_notices = false
lesson_booking.teacher = teacher
lesson_booking.lesson_type = lesson_type
- lesson_booking.lesson_booking_slots = lesson_booking_slots
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
+ end if lesson_booking_slots
+
if lesson_booking.save
- msg = ChatMessage.create(user, nil, description, ChatMessage::CHANNEL_LESSON, nil, teacher, lesson_booking)
+ msg = ChatMessage.create(user, lesson_booking.lesson_sessions[0], description, ChatMessage::CHANNEL_LESSON, nil, teacher, lesson_booking)
end
end
lesson_booking
diff --git a/ruby/lib/jam_ruby/models/lesson_booking_slot.rb b/ruby/lib/jam_ruby/models/lesson_booking_slot.rb
index 7e020f17d..91a1b7ea2 100644
--- a/ruby/lib/jam_ruby/models/lesson_booking_slot.rb
+++ b/ruby/lib/jam_ruby/models/lesson_booking_slot.rb
@@ -5,7 +5,8 @@ module JamRuby
@@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
SLOT_TYPE_SINGLE = 'single'
@@ -13,14 +14,49 @@ module JamRuby
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 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)
@@ -73,6 +109,52 @@ module JamRuby
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)
+
+ 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 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?
@@ -86,5 +168,23 @@ module JamRuby
end
end
end
+
+ def validate_slot_minimum_time
+ 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
+
+ if day_of_week
+ # this is recurring; it will sort itself out
+ else
+ time = scheduled_time(0)
+
+ if time <= minimum_start_time
+ errors.add("must be at least #{APP_CONFIG.minimum_lesson_booking_hrs} hours out from now")
+ end
+ end
+
+ end
end
end
diff --git a/ruby/lib/jam_ruby/models/lesson_package_purchase.rb b/ruby/lib/jam_ruby/models/lesson_package_purchase.rb
index 20999bbc3..b400df059 100644
--- a/ruby/lib/jam_ruby/models/lesson_package_purchase.rb
+++ b/ruby/lib/jam_ruby/models/lesson_package_purchase.rb
@@ -24,7 +24,7 @@ module JamRuby
if self.lesson_package_type.is_test_drive?
new_test_drives = user.remaining_test_drives + 4
User.where(id:user.id).update_all(remaining_test_drives: new_test_drives)
- user.remaining_test_drives = user.remaining_test_drives + 4
+ user.remaining_test_drives = new_test_drives
end
end
diff --git a/ruby/lib/jam_ruby/models/lesson_session.rb b/ruby/lib/jam_ruby/models/lesson_session.rb
index 14801ad1b..46355a17e 100644
--- a/ruby/lib/jam_ruby/models/lesson_session.rb
+++ b/ruby/lib/jam_ruby/models/lesson_session.rb
@@ -3,7 +3,7 @@ module JamRuby
class LessonSession < ActiveRecord::Base
- attr_accessor :accepting, :creating
+ attr_accessor :accepting, :creating, :countering, :countered_slot, :countered_lesson
@@log = Logging.logger[LessonSession]
@@ -24,6 +24,8 @@ module JamRuby
belongs_to :teacher, class_name: "JamRuby::User"
belongs_to :lesson_package_purchase, class_name: "JamRuby::LessonPackagePurchase"
belongs_to :lesson_booking, class_name: "JamRuby::LessonBooking"
+ belongs_to :slot, class_name: "JamRuby::LessonBookingSlot", foreign_key: :slot_id
+ has_many :lesson_booking_slots, class_name: "JamRuby::LessonBookingSlot"
validates :duration, presence: true, numericality: {only_integer: true}
validates :lesson_booking, presence: true
@@ -37,6 +39,26 @@ module JamRuby
validate :validate_creating, :if => :creating
validate :validate_accepted, :if => :accepting
+ after_save :after_counter, :if => :countering
+ after_save :manage_slot_changes, :if => :slot_changed?
+
+ def manage_slot_changes
+ # if this slot changed, we need to update the time. But LessonBooking does this for us, for requested/accepted .
+ # TODO: what to do, what to do.
+ end
+
+ def after_counter
+ send_counter(@countered_lesson, @countered_slot)
+ end
+
+ def send_counter(countered_lesson, countered_slot)
+ if countered_slot.is_teacher_created?
+ UserMailer.student_lesson_counter(countered_lesson, countered_slot).deliver
+ else
+ UserMailer.teacher_lesson_counter(countered_lesson, countered_slot).deliver
+ end
+ self.countering = false
+ end
default_scope { order(:created_at) }
@@ -56,6 +78,7 @@ module JamRuby
status == STATUS_APPROVED
end
+
def validate_creating
if !is_requested? && !is_approved?
self.errors.add(:status, "is not valid for a new lesson session.")
@@ -67,6 +90,7 @@ module JamRuby
self.errors.add(:status, "This session is already #{self.status}.")
end
+ self.accepting = false
self.status = STATUS_APPROVED
end
@@ -124,30 +148,57 @@ module JamRuby
end
end
+ def update_scheduled_start(week_offset)
+ music_session.scheduled_start = default_slot.scheduled_time(week_offset)
+ music_session.save
+ end
+
# teacher accepts the lesson
def accept(params)
LessonSession.transaction do
message = params[:message]
slot = params[:slot]
- update_all = params[:update_all]
self.accepting = true
slot = LessonBookingSlot.find(slot)
- lesson_booking.accept(self, slot, update_all)
+ lesson_booking.accept(self, slot)
self.slot = slot
if self.save
- msg = ChatMessage.create(teacher, nil, message, ChatMessage::CHANNEL_LESSON, nil, user, self)
+ msg = ChatMessage.create(teacher, nil, message, ChatMessage::CHANNEL_LESSON, nil, student, lesson_booking)
Notification.send_lesson_message('accept', self, true)
- UserMailer.student_lesson_accepted(self, message)
- UserMailer.teacher_lesson_accepted(self, message)
+ UserMailer.student_lesson_accepted(self, message).deliver
+ UserMailer.teacher_lesson_accepted(self, message).deliver
end
end
end
+ def counter(params)
+ proposer = params[:proposer]
+ slot = params[:slot]
+ message = params[:message]
+
+ self.countering = true
+ slot.proposer = proposer
+ slot.lesson_session = self
+ self.lesson_booking_slots << slot
+ self.countered_slot = slot
+ self.countered_lesson = self
+ if self.save
+ if slot.update_all || lesson_booking.is_requested?
+ lesson_booking.counter(self, proposer, slot)
+ end
+ else
+ raise ActiveRecord::Rollback
+ end
+
+ msg = ChatMessage.create(slot.proposer, music_session, message, ChatMessage::CHANNEL_LESSON, nil, slot.recipient, lesson_booking)
+ Notification.send_lesson_message('counter', self, slot.is_teacher_created?)
+ end
+
def home_url
APP_CONFIG.external_root_url + "/client#/jamclass"
end
diff --git a/ruby/lib/jam_ruby/models/lesson_session_analyser.rb b/ruby/lib/jam_ruby/models/lesson_session_analyser.rb
new file mode 100644
index 000000000..0dfcf0818
--- /dev/null
+++ b/ruby/lib/jam_ruby/models/lesson_session_analyser.rb
@@ -0,0 +1,248 @@
+require 'timespan'
+module JamRuby
+ class LessonSessionAnalyser
+
+ SUCCESS = 'success'
+ SESSION_ONGOING = 'session_ongoing'
+ THRESHOLD_MET = 'threshold_met'
+ WAITED_CORRECTLY = 'waited_correctly'
+ MINIMUM_TIME_MET = 'minimum_time_met' # for a teacher primarily; they waited around for the student sufficiently
+ LATE_CANCELLATION = 'late_cancellation'
+
+ TEACHER_FAULT = 'teacher_fault'
+ STUDENT_FAULT = 'student_fault'
+ BOTH_FAULT = 'both_fault'
+
+
+ # what are the potential results?
+
+ # bill: true/false
+
+ # teacher: 'no_show'
+ # teacher: 'late'
+ # teacher: 'early_leave'
+ # teacher: 'waited_correctly'
+ # teacher: 'late_cancellation'
+
+ # student: 'no_show'
+ # student: 'late'
+ # student: 'early_leave'
+ # student: 'minimum_time_not_met'
+ # student: 'threshold_met'
+
+
+ # reason: 'session_ongoing'
+ # reason: 'success'
+ # reason: 'student_fault'
+ # reason: 'teacher_fault'
+ # reason: 'both_fault'
+
+
+ def self.analyse(lesson_session)
+ reason = nil
+ teacher = nil
+ student = nil
+ bill = false
+
+ music_session = lesson_session.music_session
+
+ student_histories = MusicSessionUserHistory.where(music_session: music_session, user: lesson_session.student)
+ teacher_histories = MusicSessionUserHistory.where(music_session: music_session, user: lesson_session.teacher)
+
+ # create ranges from music session user history
+ all_student_ranges = time_ranges(student_histories)
+ all_teacher_ranges = time_ranges(teacher_histories)
+
+ # flatten ranges into non-overlapping ranges to simplifly logic
+ student_ranges = merge_overlapping_ranges(all_student_ranges)
+ teacher_ranges = merge_overlapping_ranges(all_teacher_ranges)
+
+ intersecting = intersecting_ranges(student_ranges, teacher_ranges)
+
+ student_analysis = analyse_intersection(lesson_session, student_ranges)
+ teacher_analysis = analyse_intersection(lesson_session, teacher_ranges)
+ together_analysis = analyse_intersection(lesson_session, intersecting)
+
+ # spec: https://jamkazam.atlassian.net/wiki/display/PS/Product+Specification+-+JamClass#ProductSpecification-JamClass-TeacherReceives&RespondstoLessonBookingRequest
+
+ if music_session.session_removed_at.nil?
+ reason = SESSION_ONGOING
+ teacher = SESSION_ONGOING
+ student = SESSION_ONGOING
+ bill = false
+ else
+
+ if lesson_session.is_canceled? && lesson_session.canceled_by_teacher? && lesson_session.canceled_late?
+ # If the lesson was cancelled less than 24 hours before the start time by the teacher, then we do not bill the student.
+ reason = TEACHER_FAULT
+ teacher = LATE_CANCELLATION
+ student = nil
+ bill = false
+ elsif lesson_session.is_canceled? && lesson_session.canceled_by_student? && lesson_session.canceled_late?
+ # If the lesson was cancelled less than 24 hours before the start time by the student (if that is even possible, I can’t remember now), then we do bill the student.
+ reason = STUDENT_FAULT
+ teacher = nil
+ student = LATE_CANCELLATION
+ bill = true
+ elsif together_analysis[:pct] >= APP_CONFIG.lesson_together_threshold_pct
+ # If the teacher and the student are together in the lesson session for at least 80% of the scheduled lesson duration, regardless of who joined/left when, then we will bill the student.
+ bill = true
+ reason = SUCCESS
+ teacher = THRESHOLD_MET
+ student = THRESHOLD_MET
+ else
+ if teacher_analysis[:joined_on_time] && teacher_analysis[:waited_correctly]
+ # if the teacher was present in the session within the first 5 minutes of the scheduled start time and stayed in the session for 10 minutes;
+ # and if either:
+
+ if student_analysis[:no_show]
+ # the student no-showed entirely, then we bill the student.
+
+ elsif student_analysis[:joined_late]
+ # the student joined the lesson more than 10 minutes after the teacher did, regardless of whether the teacher was still in the lesson session at that point; then we bill the student
+
+ elsif student_analysis[:joined_on_time]
+ # the student joined the session within 10 minutes after the teacher did, and the teacher was still there, and the teacher stays until the scheduled end time of the lesson; then we bill the student.
+ if teacher_analysis[:minimum_time_met] && teacher_analysis[:there_when_other_joined]
+
+ else
+
+ end
+ else
+ raise "unknown condition for lesson session: #{lesson_session.id}"
+ end
+
+ elsif student_analysis[:joined_on_time]
+ if student_analysis[:waited_correctly]
+ if teacher_analysis[:other_there_when_joined]
+ # bill the student
+ else
+
+ end
+
+ end
+
+ end
+
+ # if the teacher meets the time threshold, then the user is getting billed
+
+ teacher = MINIMUM_TIME_MET
+ bill = true
+
+ # What happened with the student?
+
+ if student_analysis[:waited_correctly]
+ student = WAITED_CORRECTLY
+ reason = STUDENT_FAULT
+ elsif student_analysis[:time] == 0
+ student = NO_SHOW
+ reason = STUDENT_FAULT
+ elsif student_analysis
+ end
+ end
+
+ end
+
+ {
+ reason: reason,
+ teacher: teacher,
+ student: student,
+ bill: bill,
+ student_ranges: student_ranges,
+ teacher_ranges: teacher_ranges,
+ intersecting: intersecting,
+ student_analysis: student_analysis,
+ teacher_analysis: teacher_analysis,
+ together_analysis: together_analysis,
+
+ }
+ end
+
+ def self.intersecting_ranges(ranges_a, ranges_b)
+ intersections = []
+ ranges_a.each do |range_a|
+ ranges_b.each do |range_b|
+ intersection = intersect(range_a, range_b)
+ intersections << intersection if intersection
+ end
+ end
+
+ merge_overlapping_ranges(intersections)
+ end
+
+ def self.analyse_intersection(lesson_session, ranges)
+ start = lesson_session.music_session.scheduled_start
+
+ planned_duration_seconds = lesson_session.duration * 60
+ measurable_start = start - (APP_CONFIG.lesson_analysis_slush_time_minutes * 60)
+ measurable_end = (start + planned_duration_seconds) + (APP_CONFIG.lesson_analysis_slush_time_minutes * 60)
+
+ session_time = Range.new(measurable_start, measurable_end)
+
+ # let's see how much time they spent together, irrespective of scheduled time
+ # and also, based on scheduled time
+ total = 0
+ in_scheduled_time = 0
+
+ ranges.each do |range|
+ time = range.end - range.begin
+
+ total += time
+
+ in_session_time = intersect(range, session_time)
+
+ if in_session_time
+ in_scheduled_time += in_scheduled_time.end - in_scheduled_time.begin
+ end
+ end
+
+
+
+ # percentage computation of time spent during the session time
+ in_scheduled_percentage = in_scheduled_time.to_f / planned_duration_seconds.to_f
+
+ {
+ total: total,
+ time: in_scheduled_time,
+ pct: in_scheduled_percentage
+ }
+ end
+
+ def self.intersect(a, b)
+ min, max = a.first, a.exclude_end? ? a.max : a.last
+ other_min, other_max = b.first, b.exclude_end? ? b.max : b.last
+
+ new_min = a === other_min ? other_min : b === min ? min : nil
+ new_max = a === other_max ? other_max : b === max ? max : nil
+
+ new_min && new_max ? Range.new(new_min, new_max) : nil
+ end
+
+ def self.time_ranges(histories)
+ ranges = []
+ histories.each do |history|
+ ranges << history.range
+ end
+ ranges
+ end
+
+ def self.ranges_overlap?(a, b)
+ a.include?(b.begin) || b.include?(a.begin)
+ end
+
+ def self.merge_ranges(a, b)
+ [a.begin, b.begin].min..[a.end, b.end].max
+ end
+
+ def self.merge_overlapping_ranges(ranges)
+ ranges.sort_by(&:begin).inject([]) do |ranges, range|
+ if !ranges.empty? && ranges_overlap?(ranges.last, range)
+ ranges[0...-1] + [merge_ranges(ranges.last, range)]
+ else
+ ranges + [range]
+ end
+ end
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/ruby/lib/jam_ruby/models/music_session_user_history.rb b/ruby/lib/jam_ruby/models/music_session_user_history.rb
index c8b8c0834..1fe327ba9 100644
--- a/ruby/lib/jam_ruby/models/music_session_user_history.rb
+++ b/ruby/lib/jam_ruby/models/music_session_user_history.rb
@@ -25,6 +25,10 @@ module JamRuby
user.name
end
+ def range
+ Range.new(created_at, session_removed_at || Time.now)
+ end
+
def music_session
@msh ||= JamRuby::MusicSession.find_by_music_session_id(self.music_session_id)
end
diff --git a/ruby/lib/jam_ruby/models/notification.rb b/ruby/lib/jam_ruby/models/notification.rb
index b78eceea6..dea478c7d 100644
--- a/ruby/lib/jam_ruby/models/notification.rb
+++ b/ruby/lib/jam_ruby/models/notification.rb
@@ -377,17 +377,17 @@ module JamRuby
notification = Notification.new
notification.description = NotificationTypes::LESSON_MESSAGE
notification.student_directed = student_directed
+
if !student_directed
- notification.source_user_id = lesson_session.user.id
+ notification.source_user_id = lesson_session.student.id
notification.target_user_id = lesson_session.teacher.id
else
notification.source_user_id = lesson_session.teacher.id
- notification.target_user_id = lesson_session.user.id
+ notification.target_user_id = lesson_session.student.id
end
notification.purpose = purpose
- notification.lesson_session = lesson_session
- notification.session_id = lesson_session.music_session_id
+ notification.session_id = lesson_session.music_session.id
notification_msg = 'Lesson Changed'
@@ -412,37 +412,21 @@ module JamRuby
notification.save
# receiver_id, sender_photo_url, sender_name, sender_id, msg, clipped_msg, notification_id, created_at
- message = @message_factory.lesson_message(
- notification.target.id,
- notification.source.resolved_photo_url,
- notification.source.name,
- notification.source.id,
+ message = @@message_factory.lesson_message(
+ notification.target_user.id,
+ notification.source_user.resolved_photo_url,
+ notification.source_user.name,
+ notification.source_user.id,
notification_msg,
notification.id,
- notification.lesson_session.id,
- notification.created_date
+ notification.session_id,
+ notification.created_date,
+ notification.student_directed,
+ notification.purpose
)
- if follower.id != user.id
- if user.online
- msg = @@message_factory.new_user_follower(
- user.id,
- follower.photo_url,
- notification_msg,
- notification.id,
- notification.created_date
- )
+ @@mq_router.publish_to_user(notification.target_user.id, message)
- @@mq_router.publish_to_user(user.id, msg)
-
- else
- begin
- UserMailer.new_user_follower(user, notification_msg).deliver
- rescue => e
- @@log.error("Unable to send NEW_USER_FOLLOWER email to offline user #{user.email} #{e}")
- end
- end
- end
end
def send_new_band_follower(follower, band)
diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb
index 27379b747..79b35198f 100644
--- a/ruby/lib/jam_ruby/models/user.rb
+++ b/ruby/lib/jam_ruby/models/user.rb
@@ -1898,8 +1898,10 @@ module JamRuby
lesson = nil
test_drive = nil
User.transaction do
+
lesson = card_approved(params[:token], params[:zip])
if params[:test_drive]
+ self.reload
test_drive = Sale.purchase_test_drive(self)
end
end
diff --git a/ruby/spec/factories.rb b/ruby/spec/factories.rb
index dc399cf3d..de32859ce 100644
--- a/ruby/spec/factories.rb
+++ b/ruby/spec/factories.rb
@@ -921,7 +921,7 @@ FactoryGirl.define do
factory :lesson_booking_slot, class: 'JamRuby::LessonBookingSlot' do
factory :lesson_booking_slot_single do
slot_type 'single'
- preferred_day Date.today
+ preferred_day Date.today + 3
day_of_week nil
hour 12
minute 30
diff --git a/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb b/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
index 185210814..c419ec38f 100644
--- a/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
+++ b/ruby/spec/jam_ruby/flows/testdrive_lesson_spec.rb
@@ -15,23 +15,26 @@ describe "TestDrive Lesson Flow" do
it "works" do
- # user has no test drives, but attempts to book a lesson
+ # user has no test drives, no credit card on file, but attempts to book a lesson
booking = LessonBooking.book_test_drive(user, teacher_user, valid_single_slots, "Hey I've heard of you before.")
- puts booking.errors.inspect
booking.errors.any?.should be_false
booking.card_presumed_ok.should be_false
booking.user.should eql user
booking.card_presumed_ok.should be_false
booking.should eql user.unprocessed_test_drive
+ booking.sent_notices.should be_false
- # so, they still need validate their take
+ ########## Need validate their credit card
token = create_stripe_token
- puts "UPDATING PAYMENT"
result = user.payment_update({token: token, zip: '78759', test_drive: true})
lesson = result[:lesson]
test_drive = result[:test_drive]
lesson.errors.any?.should be_false
test_drive.errors.any?.should be_false
+ lesson.card_presumed_ok.should be_true
+ lesson.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
+ booking.reload
+ booking.sent_notices.should be_true
test_drive.stripe_charge_id.should_not be_nil
test_drive.recurly_tax_in_cents.should be 412
@@ -42,7 +45,6 @@ describe "TestDrive Lesson Flow" do
line_item.quantity.should eql 1
line_item.product_type.should eql SaleLineItem::LESSON
line_item.product_id.should eq LessonPackageType.test_drive.id
-
user.reload
user.stripe_customer_id.should_not be nil
user.lesson_purchases.length.should eql 1
@@ -53,5 +55,90 @@ describe "TestDrive Lesson Flow" do
customer = Stripe::Customer.retrieve(user.stripe_customer_id)
customer.email.should eql user.email
+
+ booking.lesson_sessions.length.should eql 1
+ lesson_session = booking.lesson_sessions[0]
+ lesson_session.status.should eql LessonBooking::STATUS_REQUESTED
+ booking.status.should eql LessonBooking::STATUS_REQUESTED
+
+ ######### Teacher counters with new slot
+ teacher_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 14)
+ UserMailer.deliveries.clear
+ lesson_session.counter({proposer: teacher_user, slot: teacher_countered_slot, message: 'Does this work?'})
+ booking.errors.any?.should be false
+ lesson_session.lesson_booking.errors.any?.should be false
+ lesson_session.lesson_booking_slots.length.should eql 1
+ lesson_session.lesson_booking_slots[0].proposer.should eql teacher_user
+ teacher_counter = lesson_session.lesson_booking_slots.order(:created_at).last
+ teacher_counter.should eql teacher_countered_slot
+ teacher_counter.proposer.should eql teacher_user
+ booking.lesson_booking_slots.length.should eql 3
+ UserMailer.deliveries.length.should eql 1
+ chat = ChatMessage.unscoped.order(:created_at).last
+ chat.channel.should eql ChatMessage::CHANNEL_LESSON
+ chat.message.should eql 'Does this work?'
+ chat.user.should eql teacher_user
+ chat.target_user.should eql user
+ notification = Notification.unscoped.order(:created_at).last
+ notification.session_id.should eql lesson_session.music_session.id
+ notification.student_directed.should eql true
+ notification.purpose.should eql 'counter'
+ notification.description.should eql NotificationTypes::LESSON_MESSAGE
+ notification.message.should eql "Instructor has proposed a different time for your lesson."
+
+ ######### Student counters with new slot
+ student_countered_slot = FactoryGirl.build(:lesson_booking_slot_single, hour: 16)
+ UserMailer.deliveries.clear
+ lesson_session.counter({proposer: user, slot: student_countered_slot, message: 'Does this work better?'})
+ lesson_session.errors.any?.should be false
+ lesson_session.lesson_booking.errors.any?.should be false
+ lesson_session.lesson_booking_slots.length.should eql 2
+ student_counter = booking.lesson_booking_slots.order(:created_at).last
+ student_counter.proposer.should eql user
+ booking.reload
+ booking.lesson_booking_slots.length.should eql 4
+ UserMailer.deliveries.length.should eql 1
+ chat = ChatMessage.unscoped.order(:created_at).last
+ chat.message.should eql 'Does this work better?'
+ chat.channel.should eql ChatMessage::CHANNEL_LESSON
+ chat.user.should eql user
+ chat.target_user.should eql teacher_user
+ notification = Notification.unscoped.order(:created_at).last
+ notification.session_id.should eql lesson_session.music_session.id
+ notification.student_directed.should eql false
+ notification.purpose.should eql 'counter'
+ notification.description.should eql NotificationTypes::LESSON_MESSAGE
+ notification.message.should eql "Student has proposed a different time for your lesson."
+
+ ######## Teacher accepts slot
+ UserMailer.deliveries.clear
+
+ lesson_session.accept({message: 'Yeah I got this', slot: student_counter.id, update_all: false})
+ lesson_session.errors.any?.should be_false
+ lesson_session.reload
+ lesson_session.slot.should eql student_counter
+ lesson_session.status.should eql LessonSession::STATUS_APPROVED
+ booking.reload
+ booking.default_slot.should eql student_counter
+ lesson_session.music_session.scheduled_start.should eql booking.default_slot.scheduled_time(0)
+ booking.status.should eql LessonBooking::STATUS_APPROVED
+
+ UserMailer.deliveries.length.should eql 2
+ chat = ChatMessage.unscoped.order(:created_at).last
+ chat.message.should eql 'Yeah I got this'
+ chat.channel.should eql ChatMessage::CHANNEL_LESSON
+ chat.user.should eql teacher_user
+ chat.target_user.should eql user
+ notification = Notification.unscoped.order(:created_at).last
+ notification.session_id.should eql lesson_session.music_session.id
+ notification.student_directed.should eql true
+ notification.purpose.should eql 'accept'
+ notification.description.should eql NotificationTypes::LESSON_MESSAGE
+ notification.message.should eql "Your lesson request is confirmed!"
+
+
+ # teacher & student get into session
+
+ LessonSession
end
end
diff --git a/ruby/spec/jam_ruby/models/lesson_session_analyser_spec.rb b/ruby/spec/jam_ruby/models/lesson_session_analyser_spec.rb
new file mode 100644
index 000000000..b57939817
--- /dev/null
+++ b/ruby/spec/jam_ruby/models/lesson_session_analyser_spec.rb
@@ -0,0 +1,146 @@
+require 'spec_helper'
+
+describe LessonSessionAnalyser do
+
+ let(:user) {FactoryGirl.create(:user)}
+ let(:music_session) { FactoryGirl.create(:music_session, creator: user) }
+ let(:start) {Time.now}
+
+ describe "intersecting_ranges" do
+
+ it "empty" do
+ LessonSessionAnalyser.intersecting_ranges([], []).should eql []
+ end
+
+ it "one specified, other empty" do
+
+ uh1Begin = start
+ uh1End = start + 1
+
+ LessonSessionAnalyser.intersecting_ranges([], [Range.new(uh1Begin, uh1End)]).should eql []
+ LessonSessionAnalyser.intersecting_ranges([Range.new(uh1Begin, uh1End)], []).should eql []
+ end
+
+ it "both identical" do
+ uh1Begin = start
+ uh1End = start + 1
+
+ LessonSessionAnalyser.intersecting_ranges([Range.new(uh1Begin, uh1End)], [Range.new(uh1Begin, uh1End)]).should eql [Range.new(uh1Begin, uh1End)]
+ end
+
+ it "one intersect" do
+ uh1Begin = start
+ uh1End = start + 4
+
+ uh2Begin = start + 1
+ uh2End = start + 3
+
+ LessonSessionAnalyser.intersecting_ranges([Range.new(uh1Begin, uh1End)], [Range.new(uh2Begin, uh2End)]).should eql [Range.new(uh2Begin, uh2End)]
+ end
+
+ it "overlapping intersect" do
+ uh1Begin = start
+ uh1End = start + 4
+
+ uh2Begin = start + -1
+ uh2End = start + 2
+
+ uh3Begin = start + 3
+ uh3End = start + 5
+
+ LessonSessionAnalyser.intersecting_ranges([Range.new(uh2Begin, uh2End), Range.new(uh3Begin, uh3End)], [Range.new(uh1Begin, uh1End)]).should eql [Range.new(uh1Begin, uh2End), Range.new(uh3Begin, uh1End)]
+ end
+
+ it "no overlap" do
+ uh1Begin = start
+ uh1End = start + 4
+
+ uh2Begin = start + 5
+ uh2End = start + 6
+
+ LessonSessionAnalyser.intersecting_ranges([Range.new(uh1Begin, uh1End)], [Range.new(uh2Begin, uh2End)]).should eql []
+ end
+ end
+
+ describe "merge_overlapping_ranges" do
+
+ it "empty" do
+ LessonSessionAnalyser.merge_overlapping_ranges([]).should eql []
+ end
+
+ it "one item" do
+ uh1Begin = start
+ uh1End = start + 1
+
+ uh1 = FactoryGirl.build(:music_session_user_history, user: user, history: music_session, created_at: uh1Begin, session_removed_at: uh1End)
+
+ ranges = LessonSessionAnalyser.time_ranges [uh1]
+
+ LessonSessionAnalyser.merge_overlapping_ranges(ranges).should eql [Range.new(uh1Begin, uh1End)]
+ end
+
+ it "two identical items" do
+ uh1Begin = start
+ uh1End = start + 1
+
+ uh2Begin = uh1Begin
+ uh2End = uh1End
+
+ uh1 = FactoryGirl.build(:music_session_user_history, user: user, history: music_session, created_at: uh1Begin, session_removed_at: uh1End)
+ uh2 = FactoryGirl.build(:music_session_user_history, user: user, history: music_session, created_at: uh2Begin, session_removed_at: uh2End)
+
+ ranges = LessonSessionAnalyser.time_ranges [uh1, uh2]
+
+ LessonSessionAnalyser.merge_overlapping_ranges(ranges).should eql [Range.new(uh1Begin, uh1End)]
+ end
+
+ it "two separate times" do
+ uh1Begin = start
+ uh1End = start + 1
+
+ uh2Begin = start + 3
+ uh2End = start + 5
+
+ uh1 = FactoryGirl.build(:music_session_user_history, user: user, history: music_session, created_at: uh1Begin, session_removed_at: uh1End)
+ uh2 = FactoryGirl.build(:music_session_user_history, user: user, history: music_session, created_at: uh2Begin, session_removed_at: uh2End)
+
+ ranges = LessonSessionAnalyser.time_ranges [uh1, uh2]
+
+ LessonSessionAnalyser.merge_overlapping_ranges(ranges).should eql [Range.new(uh1Begin, uh1End), Range.new(uh2Begin, uh2End)]
+ end
+
+ it "two overlapping times" do
+ uh1Begin = start
+ uh1End = start + 1
+
+ uh2Begin = start + 0.5
+ uh2End = start + 5
+
+ uh1 = FactoryGirl.build(:music_session_user_history, user: user, history: music_session, created_at: uh1Begin, session_removed_at: uh1End)
+ uh2 = FactoryGirl.build(:music_session_user_history, user: user, history: music_session, created_at: uh2Begin, session_removed_at: uh2End)
+
+ ranges = LessonSessionAnalyser.time_ranges [uh1, uh2]
+
+ LessonSessionAnalyser.merge_overlapping_ranges(ranges).should eql [Range.new(uh1Begin, uh2End)]
+ end
+
+ it "three overlapping times" do
+ uh1Begin = start
+ uh1End = start + 1
+
+ uh2Begin = start + 0.5
+ uh2End = start + 5
+
+ uh3Begin = start + 0.1
+ uh3End = start + 6
+
+ uh1 = FactoryGirl.build(:music_session_user_history, user: user, history: music_session, created_at: uh1Begin, session_removed_at: uh1End)
+ uh2 = FactoryGirl.build(:music_session_user_history, user: user, history: music_session, created_at: uh2Begin, session_removed_at: uh2End)
+ uh3 = FactoryGirl.build(:music_session_user_history, user: user, history: music_session, created_at: uh3Begin, session_removed_at: uh3End)
+
+ ranges = LessonSessionAnalyser.time_ranges [uh1, uh2, uh3]
+
+ LessonSessionAnalyser.merge_overlapping_ranges(ranges).should eql [Range.new(uh1Begin, uh3End)]
+ end
+ end
+end
diff --git a/ruby/spec/support/utilities.rb b/ruby/spec/support/utilities.rb
index 44267d760..f1bb62842 100644
--- a/ruby/spec/support/utilities.rb
+++ b/ruby/spec/support/utilities.rb
@@ -259,7 +259,19 @@ def app_config
end
def minimum_lesson_booking_hrs
- 48
+ 24
+ end
+
+ def lesson_analysis_slush_time_minutes
+ 5
+ end
+
+ def lesson_stay_time
+ 10
+ end
+
+ def lesson_together_threshold_pct
+ 0.8
end
private
diff --git a/web/config/application.rb b/web/config/application.rb
index 7db2d108d..934026d17 100644
--- a/web/config/application.rb
+++ b/web/config/application.rb
@@ -426,6 +426,10 @@ if defined?(Bundler)
:publishable_key => 'pk_test_9vO8ZnxBpb9Udb0paruV3qLv',
:secret_key => 'sk_test_cPVRbtr9xbMiqffV8jwibwLA'
}
- config.minimum_lesson_booking_hrs = 48
- end
+ config.minimum_lesson_booking_hrs = 24
+ config.lesson_analysis_slush_time_minutes = 5
+ config.lesson_stay_time = 10
+ config.lesson_together_threshold_pct = 0.80
+
+ end
end