Fix easydropdown oopsies

This commit is contained in:
Seth Call 2016-05-07 13:45:02 -05:00
parent abf34bcd5d
commit 5ce780f03b
18 changed files with 307 additions and 29 deletions

View File

@ -843,5 +843,9 @@ module JamRuby
stats
end
def lesson_session
music_session.lesson_session
end
end
end

View File

@ -44,7 +44,6 @@ module JamRuby
has_many :chat_messages, :class_name => "JamRuby::ChatMessage", :foreign_key => "lesson_session_id"
validates :duration, presence: true, numericality: {only_integer: true}
validates :lesson_booking, presence: true
validates :lesson_type, inclusion: {in: LESSON_TYPES}
@ -73,7 +72,7 @@ module JamRuby
scope :completed, -> { where(status: STATUS_COMPLETED) }
scope :missed, -> { where(status: STATUS_MISSED) }
scope :upcoming, -> { joins(:music_session).where('music_sessions.scheduled_start > ?', Time.now) }
scope :past_cancel_window, -> { joins(:music_session).where('music_sessions.scheduled_start > ?', 24.hours.from_now ) }
scope :past_cancel_window, -> { joins(:music_session).where('music_sessions.scheduled_start > ?', 24.hours.from_now) }
def create_charge
if !is_test_drive?
@ -104,6 +103,7 @@ module JamRuby
def music_session_id
music_session.id
end
def self.hourly_check
analyse_sessions
complete_sessions
@ -141,7 +141,7 @@ module JamRuby
now = Time.zone.local_to_utc(now)
half_hour_from_now = Time.zone.local_to_utc(half_hour_from_now)
end
MusicSession.joins(lesson_session: [:lesson_booking]).where('lesson_sessions.status = ?', LessonSession::STATUS_APPROVED).where('sent_starting_notice = false').where('(scheduled_start > ? and scheduled_start < ?)', now, half_hour_from_now).each do |music_session|
lession_session = music_session.lesson_session
lession_session.send_starting_notice
@ -184,7 +184,7 @@ module JamRuby
lesson_payment_charge.amount_in_cents / 100.0
end
def analysis_to_json(analysis)
def self.analysis_to_json(analysis, preserve_object = false)
json = {}
analysis.each do |k, v|
@ -202,7 +202,11 @@ module JamRuby
json[k] = v
end
end
json.to_json
if preserve_object
json
else
json.to_json
end
end
def send_starting_notice
@ -212,6 +216,7 @@ module JamRuby
self.sent_starting_notice = true
self.save(validate: false)
end
def session_completed
LessonSession.transaction do
self.lock!

View File

@ -43,7 +43,7 @@ module JamRuby
# reason: 'both_fault'
def self.analyse(lesson_session)
def self.analyse(lesson_session, force = false)
reason = nil
teacher = nil
student = nil
@ -69,7 +69,7 @@ module JamRuby
# spec: https://jamkazam.atlassian.net/wiki/display/PS/Product+Specification+-+JamClass#ProductSpecification-JamClass-TeacherReceives&RespondstoLessonBookingRequest
if music_session.session_removed_at.nil? && !((music_session.scheduled_start + (lesson_session.duration * 60)) < Time.now)
if !force && (music_session.session_removed_at.nil? && !((music_session.scheduled_start + (lesson_session.duration * 60)) < Time.now))
reason = SESSION_ONGOING
bill = false
else

View File

@ -143,6 +143,7 @@ group :test, :cucumber do
gem 'simplecov', '~> 0.7.1'
gem 'simplecov-rcov'
gem 'capybara', '2.4.4'
gem 'rails-assets-sinon', source: 'https://rails-assets.org'
#if ENV['JAMWEB_QT5'] == '1'
# # necessary on platforms such as arch linux, where pacman -S qt5-webkit is your easiet option
# gem "capybara-webkit", :git => 'git://github.com/thoughtbot/capybara-webkit.git'

View File

@ -369,7 +369,9 @@
"JamTrackGroup": 15,
"MetronomeGroup": 16,
"MidiInputMusicGroup": 17,
"PeerMidiInputMusicGroup": 18
"PeerMidiInputMusicGroup": 18,
"UsbInputMusicGroup": 19,
"PeerUsbInputMusicGroup": 20
};
context.JK.ChannelGroupLookup = {

View File

@ -2231,6 +2231,16 @@
});
}
function getLessonAnalysis(options) {
options = options || {}
return $.ajax({
type: "GET",
url: "/api/lesson_sessions/" + options.id + "/analysis",
dataType: "json",
contentType: 'application/json'
});
}
function updateLessonSessionUnreadMessages(options) {
return $.ajax({
@ -2663,6 +2673,7 @@
this.submitStripe = submitStripe;
this.getLessonSessions = getLessonSessions;
this.getLesson = getLesson;
this.getLessonAnalysis = getLessonAnalysis;
this.updateLessonSessionUnreadMessages = updateLessonSessionUnreadMessages;
this.checkLessonCancel = checkLessonCancel;
this.checkLessonReschedule = checkLessonReschedule;

View File

@ -20,6 +20,7 @@
//= require ./react-components/stores/VideoStore
//= require ./react-components/stores/SessionStore
//= require ./react-components/stores/SessionStatsStore
//= require ./react-components/stores/BroadcastStore
//= require ./react-components/stores/ChatStore
//= require ./react-components/stores/MixerStore
//= require ./react-components/stores/ConfigureTracksStore

View File

@ -11,8 +11,11 @@ BroadcastHolder = React.createClass(
render: ->
notification = []
if this.state.notification
notification.push(`<Broadcast key={this.state.notification.id} notification={this.state.notification}/>`)
if this.state.notification.isLesson
notification.push(`<InLessonBroadcast key={'lesson'} lessonSession={this.state.notification}/>`)
else
notification.push(`<Broadcast key={this.state.notification.id} notification={this.state.notification}/>`)
`<div id="broadcast-notification-holder" className="broadcast-notification-holder" >
<ReactCSSTransitionGroup transitionName="bn-slidedown">

View File

@ -0,0 +1,62 @@
context = window
@InLessonBroadcast = React.createClass({
displayName: 'In Lesson Broadcast'
displayTime: (minuteOffset = 0) ->
untilTime = @props.lessonSession.until
timeString = ''
if untilTime.days != 0
timeString += "#{untilTime.days} days, "
if untilTime.hours != 0 || timeString.length > 0
timeString += "#{untilTime.hours} hours, "
if untilTime.minutes != 0 || timeString.length > 0
timeString += "#{untilTime.minutes + minuteOffset} minutes, "
if untilTime.seconds != 0 || timeString.length > 0
timeString += "#{untilTime.seconds} seconds"
if timeString == ''
'now!'
timeString
render: () ->
console.log("@props.lessonSession", @props.lessonSession)
if @props.lessonSession.completed
if @props.lessonSession.success
content = `<div className="message">
<p>This lesson is over.</p>
</div>`
else
content = `<div className="message">
<p>This lesson is over, but will not be billed.</p>
</div>`
else if @props.lessonSession.beforeSession
content = `<div className="message">
<p>This lesson will start in:</p>
<p className="time">{this.displayTime()}</p>
</div>`
else if @props.lessonSession.initialWindow
content = `<div className="message">
<p>You need to wait in this session for</p>
<p className="time">{this.displayTime(10)}</p>
<p>to allow time for your teacher to join you. If you leave before this timer reaches zero, and your teacher joins this session, you will be marked absent and charged for the lesson.</p>
</div>`
else if @props.lessonSession.teacherFault
if @props.lessonSession.teacherPresent?
content = `<div className="message">
<p>You may now leave the session. However, if you choose to stay in the session with the teacher, after 5 minutes together the session will be considered a success, and you will be billed.</p>
<p>If the two of you do not spend at least 5 minutes together in the session, your teacher will be marked absent and penalized for missing the lesson. You will not be charged for this lesson.</p>
</div>`
else
content = `<div className="message">
<p>You may now leave the session.</p>
<p>Your teacher will be marked absent and penalized for missing the lesson. You will not be charged for this lesson.</p>
<p>We apologize for your inconvenience, and we will work to remedy this situation.</p>
</div>`
`<div className="broadcast-notification lesson">
{content}
</div>`
})

View File

@ -27,6 +27,16 @@ context = window
found
findParticipantByUserId: (userId) ->
foundParticipant = null
for participant in @participants()
if participant.user.id == userId
foundParticipant = participant
break
foundParticipant
otherParticipants: () ->
others = []
for participant in @participants()

View File

@ -11,13 +11,116 @@ BroadcastStore = Reflux.createStore(
{
listenables: broadcastActions
currentSession: null
currentLesson: null
broadcast: null
currentLessonTimer: null
teacherFault: false
init: ->
this.listenTo(context.AppStore, this.onAppInit);
this.listenTo(context.SessionStore, this.onSessionChange)
onAppInit: (@app) ->
getTimeRemaining: (endtime) ->
t = Date.parse(endtime) - new Date().getTime()
seconds = Math.floor( (t/1000) % 60 );
minutes = Math.floor( (t/1000/60) % 60 );
hours = Math.floor( (t/(1000*60*60)) % 24 );
days = Math.floor( t/(1000*60*60*24) );
return {
'total': t,
'days': days,
'hours': hours,
'minutes': minutes,
'seconds': seconds
};
lessonTick: () ->
@timeManagement()
@changed()
timeManagement: () ->
lastCheck = $.extend({}, @currentLesson)
lessonSession = @currentLesson
lessonSession.until = @getTimeRemaining(lessonSession.scheduled_start)
if lessonSession.until.total < 0
# we are past the start time
if lessonSession.until.total < 10 * 60 * 1000 # 10 minutes
lessonSession.initialWindow = false
else
lessonSession.initialWindow = true
lessonSession.beforeSession = false
else
# we are before the due time
lessonSession.initialWindow = false
lessonSession.beforeSession = true
# if we've transitioned to a new window
if !lessonSession.beforeSession && ((lastCheck.initialWindow || !lastCheck.initialWindow?) && !lessonSession.initialWindow)
logger.debug("BroadcastStore: lesson session 'initial window' transition")
rest.getLessonAnalysis({id: lessonSession.id}).done((response) => @lessonAnalysisDone(response)).fail(@app.ajaxError)
lessonAnalysisDone: (@analysis) ->
if !@currentLesson?
logger.debug("BroadcastStore: ignoring lessonAnalysisDone")
if @analysis.status == 'completed'
@currentLesson.completed = true
@currentLesson.success = @analysis.success
@changed()
else if @analysis.analysis.reason != 'teacher_fault'
@clearLesson()
else
@teacherFault = true
@changed()
clearLesson: () ->
if @currentLesson?
@currentLesson = null
if @currentLessonTimer?
clearInterval(@currentLessonTimer)
@currentLessonTimer = null
@teacherFault = false
@changed()
onSessionChange: (session) ->
@session = session
currentSession = session.session
if currentSession? && currentSession.lesson_session? && session.inSession()
@currentSession = currentSession
lessonSession = currentSession.lesson_session
# so that receivers can check type of info coming at them via one-way events
lessonSession.isLesson = true
if lessonSession.status == 'completed'
lessonSession.completed = true
lessonSession.success = lessonSession.success
#else
# rest.getLessonAnalysis({id: lessonSession.id}).done((response) => @lessonAnalysisDone(response)).fail(@app.ajaxError)
@currentLesson = lessonSession
@timeManagement()
if !@currentLessonTimer?
@currentLessonTimer = setInterval((() => @lessonTick()), 1000)
@changed()
else
@clearLesson()
onLoad: () ->
logger.debug("loading broadcast notification...")
onLoadCompleted: (response) ->
if response.id?
logger.debug("broadcast notification sync completed")
this.trigger(response)
@broadcast = response
@changed()
onLoadFailed: (jqXHR) ->
@ -25,7 +128,16 @@ BroadcastStore = Reflux.createStore(
logger.error("broadcast notification sync failed")
onHide: () ->
this.trigger(null)
@broadcast = null
@changed()
changed: () ->
if @currentLesson?
@currentLesson.teacherFault = @teacherFault
@currentLesson.teacherPresent = @session.findParticipantByUserId(@currentLesson.teacher_id)
this.trigger(@currentLesson)
else
this.trigger(@broadcast)
}
)

View File

@ -552,6 +552,15 @@ ConfigureTracksActions = @ConfigureTracksActions
# TODO: find it via some REST API if not found?
return $.Deferred().reject().promise();
findParticipantByUserId: (userId) ->
foundParticipant = null
for participant in @participants()
if participant.user.id == userId
foundParticipant = participant
break
foundParticipant
displayWhoCreatedRecording: (clientId) ->
if @app.clientId != clientId # don't show to creator
@findUserBy({clientId: clientId})

View File

@ -366,7 +366,7 @@
padding-top: 3px;
}
+ .easydropdown-wrapper {
.easydropdown {
float:left;
margin-left:2px;
}

View File

@ -20,6 +20,17 @@
margin-left:60px;
@include border_box_sizing;
&.lesson {
p {
text-align:center;
color:$ColorTextTypical;
margin-bottom:10px;
}
.message {
width:100%;
}
}
}
.message {
float:left;
@ -27,6 +38,12 @@
font-size:12px;
}
.time {
font-size:20px;
color:white;
font-weight:bold;
}
.actions {
float:right;
text-align: right;

View File

@ -20,9 +20,25 @@ class ApiLessonSessionsController < ApiController
end
def analysis
if @lesson_session.analysed
data = JSON.parse(@lesson_session.analysis)
else
data = LessonSession.analysis_to_json(LessonSessionAnalyser.analyse(@lesson_session), true)
end
response = {
analysis: data,
status: @lesson_session.status,
success: @lesson_session.success
}
render :json => response, :status => 200
end
def start_time
if !current_user.admin
response = { message: 'not admin' }
response = {message: 'not admin'}
render :json => response, :status => 422
else
time = Time.zone.local_to_utc(Time.now + params[:minutes].to_i * 60)
@ -51,22 +67,22 @@ class ApiLessonSessionsController < ApiController
def reschedule_check
# check if within 24 hours
# check if within 24 hours
if params[:update_all]
# check if the next scheduled lesson is doable
if 24.hours.from_now > @lesson_session.lesson_booking.next_lesson.music_session.scheduled_start
response = { message: 'time_limit' }
render :json => response, :status => 422
return
end
else
if 24.hours.from_now > @lesson_session.music_session.scheduled_start
response = { message: 'time_limit' }
render :json => response, :status => 422
return
end
if params[:update_all]
# check if the next scheduled lesson is doable
if 24.hours.from_now > @lesson_session.lesson_booking.next_lesson.music_session.scheduled_start
response = {message: 'time_limit'}
render :json => response, :status => 422
return
end
else
if 24.hours.from_now > @lesson_session.music_session.scheduled_start
response = {message: 'time_limit'}
render :json => response, :status => 422
return
end
end
render :json => {}, :status => 200
end
@ -78,13 +94,13 @@ class ApiLessonSessionsController < ApiController
if params[:update_all]
# check if the next scheduled lesson is doable
if 24.hours.from_now > @lesson_session.booking.next_lesson.music_session.scheduled_start
response = { message: 'time_limit' }
response = {message: 'time_limit'}
render :json => response, :status => 422
return
end
else
if 24.hours.from_now > @lesson_session.music_session.scheduled_start
response = { message: 'time_limit' }
response = {message: 'time_limit'}
render :json => response, :status => 422
return
end
@ -112,4 +128,5 @@ class ApiLessonSessionsController < ApiController
raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
end
end
end

View File

@ -74,6 +74,10 @@ else
attributes :id, :sender_id, :receiver_id
}
child(lesson_session: :lesson_session) {
attributes :id, :scheduled_start, :status, :teacher_id, :success, :duration
}
# only show join_requests if the current_user is in the session
child({:join_requests => :join_requests}, :if => lambda { |music_session| music_session.users.exists?(current_user) } ) {
attributes :id, :text

View File

@ -696,6 +696,7 @@ SampleApp::Application.routes.draw do
match '/lesson_sessions/:id/start_time' => 'api_lesson_sessions#start_time', :via => :post
match '/lesson_sessions/:id/reschedule_check' => 'api_lesson_sessions#reschedule_check', :via => :post
match '/lesson_sessions/:id/cancel_check' => 'api_lesson_sessions#cancel_check', :via => :post
match '/lesson_sessions/:id/analysis' => 'api_lesson_sessions#analysis', :via => :get
match '/lesson_bookings' => 'api_lesson_bookings#create', :via => :post
match '/lesson_bookings/:id/accept' => 'api_lesson_bookings#accept', :via => :post
match '/lesson_bookings/:id/counter' => 'api_lesson_bookings#counter', :via => :post

View File

@ -0,0 +1,19 @@
# verifies the
describe "Lesson Session Broadcast", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
let(:user) { FactoryGirl.create(:user) }
let(:ams) { FactoryGirl.create(:active_music_session, creator: user) }
before(:each) do
ActiveMusicSession.delete_all
MusicSession.delete_all
end
it "shows before message" do
testdrive_lesson
end
end