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
56 changes: 42 additions & 14 deletions app/models/reports/region_summary_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,19 @@ def self.cache_version
def initialize(regions, periods:)
@regions = regions
@regions_by_type = regions.group_by { |region| region.region_type }
@periods = periods
@period_hash = lambda { |month_date, count| [Period.month(month_date), count] }

@original_periods =
case periods
when Range
periods
when Array
(periods.min..periods.max)
else
(periods..periods)
end

@periods = @original_periods
@period_hash = ->(month_date, count) { [Period.month(month_date), count] }
end

# Returns the earliest patient record for a Region from either assigned or registered patients. Note that this *ignores*
Expand All @@ -41,21 +52,11 @@ def initialize(regions, periods:)
# Adjusted patient counts are the patient counts from three months ago (the adjusted period) that
# are the basis for control rates. These counts DO include lost to follow up.
memoize def adjusted_patients_with_ltfu
cumulative_assigned_patients.each_with_object({}) do |(entry, result), results|
values = periods.each_with_object(Hash.new(0)) { |period, region_result|
region_result[period] = result[period.adjusted_period]
}
results[entry] = values
end
adjusted_patients_with_ltfu_for("cumulative_assigned_patients")
end

memoize def adjusted_diabetes_patients_with_ltfu
cumulative_assigned_diabetic_patients.each_with_object({}) do |(entry, result), results|
values = periods.each_with_object(Hash.new(0)) { |period, region_result|
region_result[period] = result[period.adjusted_period]
}
results[entry] = values
end
adjusted_patients_with_ltfu_for("cumulative_assigned_diabetic_patients")
end

# Adjusted patient counts are the patient counts from three months ago (the adjusted period) that
Expand Down Expand Up @@ -779,6 +780,33 @@ def diabetes_patients_by_risk_state_and_type(risk_state:, types:)

private

def adjusted_patients_with_ltfu_for(field_name)
extended_begin = @original_periods.begin.advance(
months: -Reports::REGISTRATION_BUFFER_IN_MONTHS
)
extended_range = (extended_begin..@original_periods.end)

extended_data = regions_by_type.each_with_object({}) do |(_, regions), result|
result.merge! RegionSummary.call(regions, range: extended_range)
end

cumulative_data = extended_data.transform_values do |period_values|
period_values.transform_values do |v|
v.fetch(field_name, 0)
end
end

regions.each_with_object({}) do |region, results|
counts = cumulative_data[region.slug] || {}

values = @original_periods.each_with_object(Hash.new(0)) do |period, h|
h[period] = counts.fetch(period.adjusted_period, 0)
end

results[region.slug] = values
end
end

def appts_scheduled_rates(entry)
rounded_percentages({
appts_scheduled_0_to_14_days_rates: appts_scheduled_0_to_14_days[entry.region.slug][entry.period],
Expand Down
124 changes: 124 additions & 0 deletions spec/models/reports/repository_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -934,4 +934,128 @@
expect(described_class::DELEGATED_RATES).to match_array(expected_keys)
end
end

context "period extension for 3-month lookback (18 month UI window + 3 month buffer)" do
let(:facility) { create(:facility, facility_group: facility_group_1) }

let(:end_period) { Period.month("February 2026") }
let(:start_period) { end_period.advance(months: -(Reports::MAX_MONTHS_OF_DATA - 1)) }
let(:original_range) { (start_period..end_period) }

let(:repo) { Reports::Repository.new(facility.region, periods: original_range) }

before do
allow(Reports::PatientState).to receive(:get_refresh_months).and_return(
ReportingHelpers.get_refresh_months_between_dates(
start_period.advance(months: -Reports::REGISTRATION_BUFFER_IN_MONTHS).to_date,
end_period.to_date
)
)
end

it "keeps original UI period window as 18 months" do
expect(repo.periods).to eq(original_range)
expect(repo.periods.count).to eq(18)
end

[:adjusted_diabetes_patients_with_ltfu, :adjusted_patients_with_ltfu].each do |method|
it "does not expose buffer months for #{method}" do
refresh_views
result = repo.send(method)[facility.region.slug]

expect(result.keys.min).to eq(start_period)
expect(result.keys.max).to eq(end_period)
expect(result.keys).not_to include(start_period.advance(months: -1))
expect(result.keys).not_to include(start_period.advance(months: -2))
expect(result.keys).not_to include(start_period.advance(months: -3))
end
end

it "ensures first visible month has access to 3-month back denominator data" do
patients = create_list(:patient, 3, :diabetes,
recorded_at: start_period.advance(months: -3).to_date,
assigned_facility: facility,
registration_user: user)

patients.each do |patient|
create(:blood_sugar, :with_encounter, patient: patient, facility: facility, recorded_at: start_period.to_date, user: user)
end

refresh_views
result = repo.adjusted_diabetes_patients_with_ltfu[facility.region.slug]
expect(result[start_period]).to eq(3)
end

it "works correctly for single period input with 3-month lookback data" do
single_period = Period.month("February 2026")

patients = create_list(:patient, 2, :diabetes,
recorded_at: single_period.advance(months: -3).to_date,
assigned_facility: facility,
registration_user: user)

patients.each do |patient|
create(:blood_sugar, :with_encounter,
patient: patient,
facility: facility,
recorded_at: single_period.to_date,
user: user)
end

repo = Reports::Repository.new(facility.region, periods: single_period)

refresh_views
result = repo.adjusted_diabetes_patients_with_ltfu[facility.region.slug]
expect(repo.periods).to eq(single_period..single_period)
expect(result.keys).to eq([single_period])
expect(result[single_period]).to eq(2)
end

it "ensures first visible month has access to 3-month back denominator data for hypertension" do
patients = create_list(:patient, 3, :hypertension,
recorded_at: start_period.advance(months: -3).to_date,
assigned_facility: facility,
registration_user: user)

patients.each do |patient|
create(:bp_with_encounter, patient: patient, facility: facility, recorded_at: start_period.to_date, user: user)
end

refresh_views
result = repo.adjusted_patients_with_ltfu[facility.region.slug]
expect(result[start_period]).to eq(3)
end

it "works correctly for single period input with 3-month lookback data for hypertension" do
single_period = Period.month("February 2026")

allow(Reports::PatientState).to receive(:get_refresh_months).and_return(
ReportingHelpers.get_refresh_months_between_dates(
single_period.advance(months: -Reports::REGISTRATION_BUFFER_IN_MONTHS).to_date,
single_period.to_date
)
)

patients = create_list(:patient, 2, :hypertension,
recorded_at: single_period.advance(months: -3).to_date,
assigned_facility: facility,
registration_user: user)

patients.each do |patient|
create(:bp_with_encounter,
patient: patient,
facility: facility,
recorded_at: single_period.to_date,
user: user)
end

repo = Reports::Repository.new(facility.region, periods: single_period)

refresh_views
result = repo.adjusted_patients_with_ltfu[facility.region.slug]
expect(repo.periods).to eq(single_period..single_period)
expect(result.keys).to eq([single_period])
expect(result[single_period]).to eq(2)
end
end
end