From ebabedd616d42daff4f4795489ee0e1844a7f14b Mon Sep 17 00:00:00 2001 From: Rob Sanheim Date: Tue, 8 Dec 2020 02:38:01 -0600 Subject: [PATCH] Block level reports via Regions (#1477) * Start spiking out Region backed reports * Fix duplicate districts and blocks getting spun up * Make region type creation idempotent * Ensure the slugs stay consistent w/ source slug Need to explicitly set them from the source, otherwise facility slugs are getting set to different hings. Which is oging to lead to problems and confusion. * find the correct region * Arel deprecation error for some reason... but the find is getting to the point where it should work * Delegate assigned patients to a region's source This works for now because the source is either a facility group or a facility. This wont work once we have proper block level reports * Check district region correctly * Define singlular 'foo_region?' methods for Region instances; remove plural methods this cleans up some confusion that is a result of the rails auto-generated enum methods * Get basic region reports working duck typing where possible * Get block level reports working Need to figure out the virtual vs ActiveRecord source piece on Region * Dont try to render blocks for FacilityDistrict I'm not sure if that is a valid thing to even show .../cc harimohanraj89 do you know? * linting * Less type checks * Avoid type checks * Fix logging * Add a test for block level control rates * explain this magic number * Simplify how we get assigned_patients for a Region * remove Block, dont need it anymore * Need to return a relation, not the single model treat everything as a collection of facilities so that calling code doesn't need to worry about it * Add a better error message for FG factories w/ regions Make it easier to track down dependancy issues here as we enable regions_prep more broadly * Add block spec; render views only CI * typo * back to debug in dev; info for test * linting * Update standard To pull in https://github.com/testdouble/standard/pull/228 * Remove this not needed anymore, and I don't know if itw as even working * remove dead code * better local name Co-authored-by: prabhanshuguptagit --- .rubocop.yml | 2 - Gemfile.lock | 2 +- app/controllers/reports/regions_controller.rb | 33 ++- app/models/facility.rb | 10 + app/models/facility_district.rb | 10 + app/models/facility_group.rb | 10 + app/models/region.rb | 29 +- app/services/control_rate_service.rb | 7 +- .../reports/regions/_block_tables.html.erb | 277 ++++++++++++++++++ app/views/reports/regions/show.html.erb | 9 +- .../reports/regions_controller_spec.rb | 117 ++++++++ spec/factories/facility_groups.rb | 6 + spec/models/region_spec.rb | 46 +++ spec/services/control_rate_service_spec.rb | 43 +++ spec/spec_helper.rb | 7 + spec/support/logger_helper.rb | 9 + 16 files changed, 603 insertions(+), 14 deletions(-) create mode 100644 app/views/reports/regions/_block_tables.html.erb create mode 100644 spec/support/logger_helper.rb diff --git a/.rubocop.yml b/.rubocop.yml index 13d3136285..9c005a6a08 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -15,5 +15,3 @@ Style/Documentation: Enabled: false Style/FrozenStringLiteralComment: Enabled: false -Lint/DuplicateBranch: - Enabled: false diff --git a/Gemfile.lock b/Gemfile.lock index 174dcdbd8d..467bb82dee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -502,7 +502,7 @@ GEM sshkit-interactive (0.3.0) sshkit (~> 1.12) stackprof (0.2.15) - standard (0.10.1) + standard (0.10.2) rubocop (= 1.4.2) rubocop-performance (= 1.9.1) strong_password (0.0.8) diff --git a/app/controllers/reports/regions_controller.rb b/app/controllers/reports/regions_controller.rb index 6b678cc4d9..1a2bb6acc2 100644 --- a/app/controllers/reports/regions_controller.rb +++ b/app/controllers/reports/regions_controller.rb @@ -26,12 +26,18 @@ def show @new_registrations = @last_registration_value - (@data[:cumulative_registrations].values[-2] || 0) @adjusted_registration_date = @data[:adjusted_registrations].keys[-4] - if @region.is_a?(FacilityGroup) || @region.is_a?(FacilityDistrict) + if @region.district_region? @data_for_facility = @region.facilities.each_with_object({}) { |facility, hsh| hsh[facility.name] = Reports::RegionService.new(region: facility, period: @period).call } end + if current_admin.feature_enabled?(:region_reports) && @region.district_region? && @region.respond_to?(:block_regions) + @block_data = @region.block_regions.each_with_object({}) { |region, hsh| + hsh[region.name] = Reports::RegionService.new(region: region, + period: @period).call + } + end end def details @@ -42,7 +48,7 @@ def details prev_periods: 6, include_current_period: true) - if @region.is_a?(Facility) + if @region.respond_to?(:recent_blood_pressures) @recent_blood_pressures = paginate(@region.recent_blood_pressures) end end @@ -132,6 +138,14 @@ def set_force_cache end def find_region + if current_admin.feature_enabled?("region_reports") + find_region_v2 + else + find_region_v1 + end + end + + def find_region_v1 if report_params[:report_scope] == "facility_district" @region = FacilityDistrict.new(name: report_params[:id], scope: current_admin.accessible_facilities(:view_reports)) else @@ -141,14 +155,25 @@ def find_region end end + def find_region_v2 + region_type = report_params[:report_scope] + @region = if region_type == "facility_district" + FacilityDistrict.new(name: report_params[:id], scope: current_admin.accessible_facilities(:view_reports)) + else + Region.where(region_type: region_type).find_by!(slug: report_params[:id]) + end + end + def region_class @region_class ||= case report_params[:report_scope] + when "facility_district" + "facility_district" when "district" "facility_group" when "facility" "facility" - when "facility_district" - "facility_district" + when "block" + "block" else raise ActiveRecord::RecordNotFound, "unknown report scope #{report_params[:report_scope]}" end diff --git a/app/models/facility.rb b/app/models/facility.rb index e52693c3d6..01489997e8 100644 --- a/app/models/facility.rb +++ b/app/models/facility.rb @@ -200,6 +200,16 @@ def discardable? registered_patients.none? && blood_pressures.none? && blood_sugars.none? && appointments.none? end + # For regions compatibility + def facility_region? + true + end + + # For regions compatibility + def district_region? + false + end + def valid_block unless facility_group.region.block_regions.pluck(:name).include?(block) errors.add(:zone, "not present in the facility group") diff --git a/app/models/facility_district.rb b/app/models/facility_district.rb index e09f9ed90e..29a481f0bb 100644 --- a/app/models/facility_district.rb +++ b/app/models/facility_district.rb @@ -58,4 +58,14 @@ def model_name def updated_at facilities.maximum(:updated_at) || Time.current.beginning_of_day end + + # For regions compatibility + def facility_region? + false + end + + # For regions compatibility + def district_region? + true + end end diff --git a/app/models/facility_group.rb b/app/models/facility_group.rb index a168aa47e6..f8e9f2a112 100644 --- a/app/models/facility_group.rb +++ b/app/models/facility_group.rb @@ -91,6 +91,16 @@ def cohort_analytics(period:, prev_periods:) query.call end + # For regions compatibility + def facility_region? + false + end + + # For regions compatibility + def district_region? + true + end + private def set_diabetes_management(value) diff --git a/app/models/region.rb b/app/models/region.rb index 71efc17c88..6679b773be 100644 --- a/app/models/region.rb +++ b/app/models/region.rb @@ -22,6 +22,16 @@ class Region < ApplicationRecord REGION_TYPES = %w[root organization state district block facility].freeze enum region_type: REGION_TYPES.zip(REGION_TYPES).to_h, _suffix: "regions" + REGION_TYPES.each do |type| + # Our enum adds a pluralized suffix, which is nice for scopes, but weird for the question methods + # with individual objects. So we define our own question methods here for a nicer API. + define_method("#{type}_region?") do + region_type == type + end + # Don't leave around the old, auto generated methods to avoid confusion + undef_method "#{type}_regions?" + end + def self.root Region.find_by(region_type: :root) end @@ -38,6 +48,19 @@ def short_uuid SecureRandom.uuid[0..7] end + def assigned_patients + Patient.where(assigned_facility: facilities) + end + + def facilities + if facility_region? + Facility.where(id: source_id) + else + source_ids = facility_regions.pluck(:source_id) + Facility.where(id: source_ids) + end + end + # A label is a sequence of alphanumeric characters and underscores. # (In C locale the characters A-Za-z0-9_ are allowed). # Labels must be less than 256 bytes long. @@ -62,18 +85,18 @@ def log_payload if self_and_descendant_types(region_type).include?(self.region_type) self_and_ancestors.find_by(region_type: region_type) else - raise NoMethodError, "undefined method #{region_type} for #{self} of type #{self.region_type}" + raise NoMethodError, "undefined method #{region_type} for region '#{name}' of type #{self.region_type}" end end # Generates has_many type of methods to fetch a region's descendants - # e.g. organization.facilities + # e.g. organization.facility_regions descendant_method = "#{region_type}_regions" define_method(descendant_method) do if ancestor_types(region_type).include?(self.region_type) descendants.where(region_type: region_type) else - raise NoMethodError, "undefined method #{region_type.pluralize} for #{self} of type #{self.region_type}" + raise NoMethodError, "undefined method #{region_type.pluralize} for region '#{name}' of type #{self.region_type}" end end end diff --git a/app/services/control_rate_service.rb b/app/services/control_rate_service.rb index b8ad41921b..4cc32b4ac0 100644 --- a/app/services/control_rate_service.rb +++ b/app/services/control_rate_service.rb @@ -19,8 +19,8 @@ def initialize(region, periods:) end @quarterly_report = @report_range.begin.quarter? @results = Reports::Result.new(region: @region, period_type: @report_range.begin.type) - logger.info class: self.class, msg: "created", region: region.id, region_name: region.name, - report_range: report_range.inspect, facilities: facilities.map(&:id) + logger.info class: self.class.name, msg: "created", region: region.id, region_name: region.name, + report_range: report_range.inspect, facilities: facilities.map(&:id), cache_key: cache_key end delegate :logger, to: Rails @@ -63,7 +63,8 @@ def fetch_all_data def registration_counts return @registration_counts if defined? @registration_counts formatter = lambda { |v| quarterly_report? ? Period.quarter(v) : Period.month(v) } - @registration_counts = region.assigned_patients.with_hypertension.group_by_period(report_range.begin.type, :recorded_at, {format: formatter}).count + @registration_counts = region.assigned_patients.with_hypertension.group_by_period(report_range.begin.type, + :recorded_at, {format: formatter}).count end def controlled_patients(period) diff --git a/app/views/reports/regions/_block_tables.html.erb b/app/views/reports/regions/_block_tables.html.erb new file mode 100644 index 0000000000..f7ef021133 --- /dev/null +++ b/app/views/reports/regions/_block_tables.html.erb @@ -0,0 +1,277 @@ +

+ Blocks in <%= @region.name %> +

+

+ Total: <%= @region.facilities.length %> facilities +

+
+
+
+
+

+ BP controlled +

+
+
+ + + + + + + + + + + + + + + + + + + + <% data.values.map(&:region).each do |region| %> + + + + + + <% end %> + +
+ Facility + + % of patients with BP controlled + + Total patients with BP controlled +
+ <%= @region.name %> + + <%= number_to_percentage(@data.last_value(:controlled_patients_rate), precision: 0) %> + + <%= number_with_delimiter(@data.last_value(:controlled_patients)) %> +
+ <%= link_to(reports_region_path(region, report_scope: region_type))do %> + <%= region.name %> + <% end %> + + <%= number_to_percentage(data[region.name]["controlled_patients_rate"].values.last, precision: 0) %> + + <%= number_with_delimiter(data[region.name]["controlled_patients"].values.last) %> +
+
+
+

+ Numerator: Patients with BP <140/90 at their most recent visit in the last 3 months +

+

+ Denominator: All hypertensive patients assigned to a facility in <%= @region.name %> registered before + the last 3 months +

+
+
+
+
+
+
+

+ BP not controlled +

+
+
+ + + + + + + + + + + + + + + + + + + + <% data.values.map(&:region).each do |region| %> + + + + + + <% end %> + +
+ Facility + + % of patients with BP not controlled + + Total patients with BP not controlled +
+ <%= @region.name %> + + <%= number_to_percentage(@data.last_value(:uncontrolled_patients_rate), precision: 0) %> + + <%= number_with_delimiter(@data.last_value(:uncontrolled_patients)) %> +
+ <%= link_to(reports_region_path(region, report_scope: region_type))do %> + <%= region.name %> + <% end %> + + <%= number_to_percentage(data[region.name]["uncontrolled_patients_rate"].values.last, precision: 0) %> + + <%= number_with_delimiter(data[region.name]["uncontrolled_patients"].values.last) %> +
+
+
+

+ Numerator: Patients with BP ≥140/90 at their most recent visit in the last 3 months +

+

+ Denominator: All hypertensive patients assigned to a facility in <%= @region.name %> registered before + the last 3 months +

+
+
+
+
+
+
+

+ Missed visits +

+
+
+ + + + + + + + + + + + + + + + + + + + <% data.values.map(&:region).each do |region| %> + + + + + + <% end %> + +
+ Facility + + % of patients with no visit in >3 months + + Total patients with no visit in >3 months +
+ <%= @region.name %> + + <%= number_to_percentage(@data.last_value(:missed_visits_rate), precision: 0) %> + + <%= number_with_delimiter(@data.last_value(:missed_visits)) %> +
+ <%= link_to(reports_region_path(region, report_scope: region_type))do %> + <%= region.name %> + <% end %> + + <%= data[region.name]["missed_visits_rate"].values.last %>% + + <%= data[region.name]["missed_visits"].values.last %> +
+
+
+

+ Numerator: Patients with no visit in >3 months +

+

+ Denominator: All hypertensive patients assigned to a facility in <%= @region.name %> registered before + the last 3 months +

+
+
+
+
+
+
+

+ Monthly and cumulative patients +

+
+
+ + + + + + + + + + + + + + + + + + + + <% data.values.map(&:region).each do |region| %> + + + + + + <% end %> + +
+ Facility + + Patients registered in <%= @period.to_s %> + + Cumulative patients +
+ <%= @region.name %> + + <%= number_with_delimiter(@data.last_value(:registrations)) %> + + <%= number_with_delimiter(@data.last_value(:cumulative_registrations)) %> +
+ <%= link_to(reports_region_path(region, report_scope: region_type))do %> + <%= region.name %> + <% end %> + + <%= number_with_delimiter(data[region.name]["registrations"].values.last || 0) %> + + <%= number_with_delimiter(data[region.name]["cumulative_registrations"].values.last) %> +
+
+
+

+ Denominator: All hypertensive patients assigned to a facility in <%= @region.name %> registered before + the last 3 months +

+
+
+
+
diff --git a/app/views/reports/regions/show.html.erb b/app/views/reports/regions/show.html.erb index 5994041ba0..94c5a8d2be 100644 --- a/app/views/reports/regions/show.html.erb +++ b/app/views/reports/regions/show.html.erb @@ -293,10 +293,17 @@ + <%= render "visit_details_card" %> -<% unless @region.is_a?(Facility) %> + +<% if current_admin.feature_enabled?(:region_reports) && @region.district_region? && @region.respond_to?(:block_regions) %> + <%= render "block_tables", data: @block_data, region_type: "block" %> +<% end %> + +<% if @region.district_region? %> <%= render "facility_tables" %> <% end %> + diff --git a/spec/controllers/reports/regions_controller_spec.rb b/spec/controllers/reports/regions_controller_spec.rb index 11ec8b7768..b1806616f6 100644 --- a/spec/controllers/reports/regions_controller_spec.rb +++ b/spec/controllers/reports/regions_controller_spec.rb @@ -175,6 +175,123 @@ def refresh_views end end + context "show v2" do + render_views_on_ci + + before do + Flipper.enable(:regions_prep) + Flipper.enable_actor(:region_reports, cvho) + @facility_group = create(:facility_group, organization: organization) + @facility = create(:facility, name: "CHC Barnagar", facility_group: @facility_group) + end + + it "raises error if matching region slug found" do + expect { + sign_in(cvho.email_authentication) + get :show, params: {id: "String-unknown", report_scope: "bad-report_scope"} + }.to raise_error(ActiveRecord::RecordNotFound) + end + + it "returns period info for every month" do + Timecop.freeze("June 1 2020") do + sign_in(cvho.email_authentication) + get :show, params: {id: @facility.facility_group.slug, report_scope: "district"} + end + data = assigns(:data) + period_hash = { + "name" => "Dec-2019", + "bp_control_start_date" => "1-Oct-2019", + "bp_control_end_date" => "31-Dec-2019" + } + expect(data[:period_info][dec_2019_period]).to eq(period_hash) + end + + it "retrieves district data" do + jan_2020 = Time.parse("January 1 2020") + patient = create(:patient, registration_facility: @facility, recorded_at: jan_2020.advance(months: -4)) + create(:blood_pressure, :under_control, recorded_at: jan_2020.advance(months: -1), patient: patient, facility: @facility) + create(:blood_pressure, :hypertensive, recorded_at: jan_2020, facility: @facility) + refresh_views + + fg = @facility.facility_group + expect(fg.region).to_not be_nil + expect(fg.slug).to eq(fg.region.slug) + expect(fg.region.facilities).to contain_exactly(@facility) + + Timecop.freeze("June 1 2020") do + sign_in(cvho.email_authentication) + get :show, params: {id: @facility.facility_group.slug, report_scope: "district"} + end + expect(response).to be_successful + data = assigns(:data) + expect(data[:controlled_patients].size).to eq(9) # sanity check + expect(data[:controlled_patients][dec_2019_period]).to eq(1) + end + + it "retrieves facility data" do + jan_2020 = Time.parse("January 1 2020") + patient = create(:patient, registration_facility: @facility, recorded_at: jan_2020.advance(months: -4)) + create(:blood_pressure, :under_control, recorded_at: jan_2020.advance(months: -1), patient: patient, facility: @facility) + create(:blood_pressure, :hypertensive, recorded_at: jan_2020, facility: @facility) + refresh_views + + Timecop.freeze("June 1 2020") do + sign_in(cvho.email_authentication) + get :show, params: {id: @facility.slug, report_scope: "facility"} + end + expect(response).to be_successful + data = assigns(:data) + expect(data[:controlled_patients].size).to eq(9) # sanity check + expect(data[:controlled_patients][Date.parse("Dec 2019").to_period]).to eq(1) + end + + it "retrieves block data" do + jan_2020 = Time.parse("January 1 2020") + + patient_2 = create(:patient, registration_facility: @facility, recorded_at: "June 01 2019 00:00:00 UTC", registration_user: cvho) + create(:blood_pressure, :hypertensive, recorded_at: "Feb 2020", facility: @facility, patient: patient_2, user: cvho) + + patient_1 = create(:patient, registration_facility: @facility, recorded_at: "September 01 2019 00:00:00 UTC", registration_user: cvho) + create(:blood_pressure, :under_control, recorded_at: "December 10th 2019", patient: patient_1, facility: @facility, user: cvho) + create(:blood_pressure, :hypertensive, recorded_at: jan_2020, facility: @facility, user: cvho) + + refresh_views + + block = @facility.region.block_region + Timecop.freeze("June 1 2020") do + sign_in(cvho.email_authentication) + get :show, params: {id: block.to_param, report_scope: "block"} + end + expect(response).to be_successful + data = assigns(:data) + + expect(data[:registrations][Period.month("June 2019")]).to eq(1) + expect(data[:registrations][Period.month("September 2019")]).to eq(1) + expect(data[:controlled_patients][Period.month("Dec 2019")]).to eq(1) + expect(data[:uncontrolled_patients][Period.month("Feb 2020")]).to eq(1) + expect(data[:uncontrolled_patients_rate][Period.month("Feb 2020")]).to eq(50) + expect(data[:missed_visits][Period.month("September 2019")]).to eq(1) + expect(data[:missed_visits][Period.month("May 2020")]).to eq(2) + end + + it "retrieves facility district data" do + jan_2020 = Time.parse("January 1 2020") + patient = create(:patient, registration_facility: @facility, recorded_at: jan_2020.advance(months: -4)) + create(:blood_pressure, :under_control, recorded_at: jan_2020.advance(months: -1), patient: patient, facility: @facility) + create(:blood_pressure, :hypertensive, recorded_at: jan_2020, facility: @facility) + refresh_views + + Timecop.freeze("June 1 2020") do + sign_in(cvho.email_authentication) + get :show, params: {id: @facility.district, report_scope: "facility_district"} + end + expect(response).to be_successful + data = assigns(:data) + expect(data[:controlled_patients].size).to eq(9) # sanity check + expect(data[:controlled_patients][dec_2019_period]).to eq(1) + end + end + context "download" do render_views diff --git a/spec/factories/facility_groups.rb b/spec/factories/facility_groups.rb index 291e05f743..c1f4201565 100644 --- a/spec/factories/facility_groups.rb +++ b/spec/factories/facility_groups.rb @@ -18,6 +18,12 @@ before(:create) do |facility_group, options| if options.create_parent_region + if facility_group.organization.region.nil? + raise ArgumentError, <<-EOL.strip_heredoc + The facility group's organization lacks a region, which is required for regions_prep. + Check the ordering of fixtures, something was probably created before the regions_prep flag was enabled." + EOL + end facility_group.organization.region.state_regions.find_by(name: facility_group.state) || create(:region, :state, name: facility_group.state, reparent_to: facility_group.organization.region) end diff --git a/spec/models/region_spec.rb b/spec/models/region_spec.rb index c159e17130..cbb7112076 100644 --- a/spec/models/region_spec.rb +++ b/spec/models/region_spec.rb @@ -25,6 +25,52 @@ end end + context "region_type" do + it "has question methods for determining type" do + region_1 = Region.create!(name: "New York", region_type: "state", reparent_to: Region.root) + region_2 = Region.create!(name: "New York", region_type: "district", reparent_to: region_1) + region_3 = Region.create!(name: "New York", region_type: "block", reparent_to: region_2) + region_4 = Region.create!(name: "New York", region_type: "facility", reparent_to: region_3) + expect(region_1).to be_state_region + expect(region_2.district_region?).to be_truthy + expect(region_3.block_region?).to be_truthy + expect(region_4.facility_region?).to be_truthy + end + end + + context "facilities" do + it "returns the source facilities" do + facility_group = create(:facility_group) + facilities = create_list(:facility, 3, block: "Block ABC", facility_group: facility_group) + RegionBackfill.call(dry_run: false) + + facility = facilities.first + block_region = facility.region.parent + district_region = block_region.parent + expect(block_region.facilities).to contain_exactly(*facilities) + expect(district_region.facilities).to contain_exactly(*facilities) + expect(district_region.organization_region.facilities).to contain_exactly(*facilities) + end + + it "gets assigned patients via facilities" do + facility_group = create(:facility_group) + block_1_facilities = create_list(:facility, 3, block: "Block 1", facility_group: facility_group) + block_2_facility = create(:facility, block: "Block 2", facility_group: facility_group) + patients_in_block_1 = block_1_facilities.each_with_object([]) { |facility, ary| + ary << create(:patient, registration_facility: facility) + } + patients_in_block_2 = create_list(:patient, 2, registration_facility: block_2_facility) + RegionBackfill.call(dry_run: false) + block_1 = Region.block_regions.find_by!(name: "Block 1") + block_2 = Region.block_regions.find_by!(name: "Block 2") + expect(block_2_facility.region.assigned_patients).to match_array(patients_in_block_2) + expect(block_1.assigned_patients).to match_array(patients_in_block_1) + expect(block_2.assigned_patients).to match_array(patients_in_block_2) + expect(facility_group.region.assigned_patients).to match_array(patients_in_block_1 + patients_in_block_2) + expect(facility_group.region.state_region.assigned_patients).to match_array(patients_in_block_1 + patients_in_block_2) + end + end + context "behavior" do it "sets a valid path" do enable_flag(:regions_prep) diff --git a/spec/services/control_rate_service_spec.rb b/spec/services/control_rate_service_spec.rb index 9bd0960c4d..f873d76de6 100644 --- a/spec/services/control_rate_service_spec.rb +++ b/spec/services/control_rate_service_spec.rb @@ -246,6 +246,49 @@ def refresh_views expect(result[:controlled_patients_rate][Period.month(jan_2020)]).to eq(33) end + it "returns registrations and control rates for a block" do + enable_flag(:regions_prep) + brooklyn_facilities = FactoryBot.create_list(:facility, 2, block: "Brooklyn", facility_group: facility_group_1) + queens_facility = FactoryBot.create(:facility, block: "Queens", facility_group: facility_group_1) + facility_1, facility_2 = brooklyn_facilities[0], brooklyn_facilities[1] + + controlled_in_facility_1 = create_list(:patient, 2, full_name: "controlled", recorded_at: jan_2019, + registration_facility: facility_1, registration_user: user) + uncontrolled_in_facility_1 = create_list(:patient, 4, full_name: "uncontrolled", recorded_at: jan_2019, + registration_facility: facility_1, registration_user: user) + controlled_in_facility_2 = create_list(:patient, 2, full_name: "other facility", recorded_at: jan_2019, + registration_facility: facility_2, registration_user: user) + patient_from_other_block = create(:patient, full_name: "other block", recorded_at: jan_2019, + registration_facility: queens_facility, registration_user: user) + + # sanity checks for proper amount of regions + expect(Region.state_regions.count).to eq(1) + expect(Region.count).to eq(9) # 1 root, 1 org, 1 state, 1 district, 2 blocks, 3 facilities + + Timecop.freeze(jan_2020) do + (controlled_in_facility_1 + controlled_in_facility_2).map do |patient| + create(:blood_pressure, :under_control, facility: patient.registration_facility, patient: patient, recorded_at: 2.days.ago, user: user) + create(:blood_pressure, :hypertensive, facility: patient.registration_facility, patient: patient, recorded_at: 4.days.ago, user: user) + end + uncontrolled_in_facility_1.map do |patient| + create(:blood_pressure, :hypertensive, facility: patient.registration_facility, + patient: patient, recorded_at: 4.days.ago, user: user) + end + create(:blood_pressure, :under_control, facility: queens_facility, patient: patient_from_other_block, + recorded_at: 2.days.ago, user: user) + end + + refresh_views + + periods = Period.month(july_2018)..Period.month(july_2020) + service = ControlRateService.new(facility_1.block_region, periods: periods) + result = service.call + + expect(result[:registrations][Period.month(jan_2019)]).to eq(8) + expect(result[:controlled_patients][Period.month(jan_2020)]).to eq(4) + expect(result[:controlled_patients_rate][Period.month(jan_2020)]).to eq(50) + end + context "caching" do let(:memory_store) { ActiveSupport::Cache.lookup_store(:redis_store) } let(:cache) { Rails.cache } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d6bd515330..f4b7d71d0a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,6 +13,13 @@ config.filter_run focus: true config.run_all_when_everything_filtered = true + # Rendering views in controller specs adds significant overhead and time - so sometimes it is useful to + # only render_views on CI, especially if you are using guard and re-running focused specs. + # For example, on an iMac Pro it takes about 2.8 seconds to run a single controller spec w/o views, and 6 seconds to run w/ views. + def render_views_on_ci + render_views if ENV["CI"] + end + config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end diff --git a/spec/support/logger_helper.rb b/spec/support/logger_helper.rb new file mode 100644 index 0000000000..064ea2eb02 --- /dev/null +++ b/spec/support/logger_helper.rb @@ -0,0 +1,9 @@ +module LoggerHelper + def logger + Rails.logger + end +end + +RSpec.configure do |config| + config.include LoggerHelper +end