Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion app/models/segment_times_container.rb
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,8 +21,13 @@ def segment_time(segment)
def limits(segment)
return @limits_hashes[segment] if @limits_hashes.key?(segment)

typical_time = segment_time(segment)
@limits_hashes[segment] =
segment_time(segment) ? DataStatus.limits(segment_time(segment), limits_type(segment)) : {}
if typical_time.nil? || (!typical_time.positive? && scaling_limits_type?(segment))
{}
else
DataStatus.limits(typical_time, limits_type(segment))
end
end

def data_status(segment, seconds)
Expand All@@ -37,6 +42,13 @@ def limits_type(segment)
segment.special_limits_type || calc_model
end

# zero_start bands are intentionally all-zero, and in_aid adds a fixed
# positive allowance, so a zero typical time still yields a usable band
# for those types; all others scale purely with the typical time
def scaling_limits_type?(segment)
!limits_type(segment).to_s.in?(%w[zero_start in_aid])
end

def validate_setup
if calc_model == :focused && effort_ids.nil?
raise ArgumentError,
Expand Down
7 changes: 6 additions & 1 deletion app/queries/split_time_query.rb
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,9 +16,12 @@ def self.typical_segment_time(segment, effort_ids)
(select extract(epoch from(st2.absolute_time - st1.absolute_time)) as seconds
from (select st.effort_id, st.absolute_time
from split_times st
inner join efforts ef on ef.id = st.effort_id
inner join events ev on ev.id = ef.event_id
where st.lap = #{begin_lap}
and st.split_id = #{begin_id}
and st.sub_split_bitkey = #{begin_bitkey}
and ev.use_for_projections is true
and (st.data_status in (#{valid_statuses_list}) or st.data_status is null))
as st1,
(select st.effort_id, st.absolute_time
Expand All@@ -28,7 +31,9 @@ def self.typical_segment_time(segment, effort_ids)
and st.sub_split_bitkey = #{end_bitkey}
and (st.data_status in (#{valid_statuses_list}) or st.data_status is null))
as st2
where st1.effort_id = st2.effort_id and #{focus_clause}),
where st1.effort_id = st2.effort_id
and st2.absolute_time >= st1.absolute_time
and #{focus_clause}),

quartiles as
(select percentile_cont(0.25) within group (order by seconds) as q1,
Expand Down
4 changes: 3 additions & 1 deletion app/services/simulate_in_progress_event_group.rb
Original file line numberDiff line numberDiff line change
Expand Up@@ -52,7 +52,9 @@ def build_event_group
end

shift = start_time - new_event_group.scheduled_start_time
new_event_group.events.each { |event| event.update!(scheduled_start_time: event.scheduled_start_time + shift) }
new_event_group.events.each do |event|
event.update!(scheduled_start_time: event.scheduled_start_time + shift, use_for_projections: false)
end
new_event_group.update!(available_live: true)
end

Expand Down
6 changes: 6 additions & 0 deletions spec/models/duplicate_event_group_spec.rb
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,6 +32,12 @@
it "sets created_by to the provided user" do
expect(subject.new_event_group.created_by).to eq(user.id)
end

it "keeps use_for_projections on the duplicated events" do
# UI duplicates are next year's real races; their live times must
# feed projections and statistics when the race runs
expect(subject.new_event_group.events).to all(have_attributes(use_for_projections: true))
end
end

context "when the source event group has a webhook token" do
Expand Down
21 changes: 21 additions & 0 deletions spec/models/segment_times_container_spec.rb
Original file line numberDiff line numberDiff line change
Expand Up@@ -143,6 +143,27 @@ def validate_segment_time(segment, calc_model, expected, effort_ids = nil)
expect(container.limits(segment)).to eq({})
end

it "returns an empty hash instead of a degenerate band when a scaling typical time is zero" do
segment = lap_1_start_to_lap_2_finish
container = described_class.new(calc_model: :stats)
allow(container).to receive(:segment_time).and_return(0.0)
expect(container.limits(segment)).to eq({})
end

it "returns an empty hash when a scaling typical time is negative" do
segment = lap_1_start_to_lap_2_finish
container = described_class.new(calc_model: :stats)
allow(container).to receive(:segment_time).and_return(-100.0)
expect(container.limits(segment)).to eq({})
end

it "still returns a usable band for an in-aid segment with a zero typical time" do
segment = lap_1_in_aid_1
container = described_class.new(calc_model: :stats)
allow(container).to receive(:segment_time).and_return(0.0)
expect(container.limits(segment)[:high_bad]).to be_positive
end

def validate_limits(segment, expected_time, expected_limits_type, calc_model)
allow(DataStatus).to receive(:limits)
container = SegmentTimesContainer.new(calc_model: calc_model, effort_ids: [1, 2, 3])
Expand Down
42 changes: 42 additions & 0 deletions spec/queries/split_time_query_spec.rb
Original file line numberDiff line numberDiff line change
Expand Up@@ -67,5 +67,47 @@ def execute_query
expect(time).to be_within(100).of(300)
end
end

context "when the event is not used for projections" do
before { events(:hardrock_2015).update_column(:use_for_projections, false) }

context "when effort_ids are not provided" do
let(:segment) { start_to_cunningham_in }
let(:effort_ids) { nil }

it "excludes the event's efforts from the pool" do
expect(count).to eq(0)
expect(time).to be_nil
end
end

context "when effort_ids are provided" do
let(:event) { events(:hardrock_2015) }
let(:segment) { in_aid_sherman }
let(:effort_ids) { event.efforts.order(:bib_number).ids.first(2) }

it "excludes the event's efforts even when focused" do
expect(count).to eq(0)
expect(time).to be_nil
end
end
end

context "when a segment time is negative" do
let(:segment) { in_aid_sherman }
let(:effort_ids) { [effort.id] }
let(:effort) { events(:hardrock_2015).efforts.order(:bib_number).first }

before do
in_time = effort.split_times.find_by(split: sherman_split, bitkey: in_bitkey)
out_time = effort.split_times.find_by(split: sherman_split, bitkey: out_bitkey)
out_time.update_column(:absolute_time, in_time.absolute_time - 1.minute)
end

it "excludes the negative pair from the pool" do
expect(count).to eq(0)
expect(time).to be_nil
end
end
end
end
4 changes: 4 additions & 0 deletions spec/services/simulate_in_progress_event_group_spec.rb
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,6 +18,10 @@
expect(result.new_event_group.name).to include("Simulated")
end

it "excludes the simulated events from projections and statistics" do
expect(result.new_event_group.events).to all(have_attributes(use_for_projections: false))
end

it "places the group start at the requested start time" do
expect(result.new_event_group.events.map(&:scheduled_start_time).min).to be_within(1.second).of(start_time)
end
Expand Down
Loading