Skip to content

Commit ff474e0

Browse files
feat: Dr. Rai Reports (#5621)
**Story card:** [sc-15494](https://app.shortcut.com/simpledotorg/story/15494/create-targets) ## Because Dr. Rai Reports ## This addresses Building out the critical paths of Dr. Rai. This is not the full feature. ## Test instructions suite-test --------- Co-authored-by: Jamie Carter <[email protected]>
1 parent 8b9a405 commit ff474e0

36 files changed

+1455
-465
lines changed

app/assets/stylesheets/application.scss

Lines changed: 212 additions & 294 deletions
Large diffs are not rendered by default.

app/components/dashboard/dr_rai_report.html.erb

Lines changed: 267 additions & 136 deletions
Large diffs are not rendered by default.

app/components/dashboard/dr_rai_report.rb

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,41 @@
11
# Root component for Dr. Rai Reports
22
class Dashboard::DrRaiReport < ApplicationComponent
3-
# FIXME(:selected_period): Write out JS for this component's interactivity.
4-
# The whole current period thing only works onMount — sticking to frontend
5-
# terms here — after then, it's useless. This is because a view-component in
6-
# Rails, which this is, is technically a server-side render scoped to its own
7-
# local variables, and exposing methods as callable from the view. It doesn't
8-
# handle state. This means interactivity cannot be handled at the view
9-
# component layer. In lay man's terms, when someone selects another quarter
10-
# to view, we need to do a full page refresh if we are depending on the view
11-
# component; a full page refresh passing in the selected quarter. We need JS
12-
13-
attr_reader :quarterlies
3+
attr_reader :quarterlies, :indicators, :region, :action_plans
144
attr_accessor :selected_period
155

16-
def initialize(quarterlies, region, selected_quarter = nil)
6+
def initialize(quarterlies, region_slug, selected_quarter = nil)
177
@quarterlies = quarterlies
18-
@region = region
8+
@region = Region.find_by(slug: region_slug)
199
@selected_period = if selected_quarter.nil?
20-
current_period
10+
Period.new(type: :quarter, value: current_period.value.to_s)
2111
else
2212
Period.new(type: :quarter, value: selected_quarter)
2313
end
24-
@goals = {}
14+
@action_plans = DrRai::ActionPlan
15+
.includes(:dr_rai_target)
16+
.where(
17+
region: @region,
18+
dr_rai_target: {period: @selected_period.value.to_s}
19+
)
20+
@indicators = DrRai::Indicator.all
21+
end
22+
23+
def indicator_previous_numerator(indicator)
24+
indicator.numerator(region, selected_period.previous)
25+
end
26+
27+
def indicator_denominator(indicator)
28+
indicator.denominator(region, selected_period)
2529
end
2630

2731
def current_period
2832
Period.current.to_quarter_period
2933
end
3034

35+
def current_period?
36+
current_period == selected_period
37+
end
38+
3139
def start_of period
3240
period.begin.strftime("%b-%-d")
3341
end
@@ -38,15 +46,11 @@ def end_of period
3846

3947
def classes_for_period period
4048
raise "#{period} is not a Period" unless period.is_a? Period
41-
candidates = ["actions-header-button"]
42-
candidates << "action-header-selected" if period == selected_period
49+
candidates = ["period-button"]
50+
candidates << "selected" if period == selected_period
4351
candidates.join(" ")
4452
end
4553

46-
def period_goals
47-
@goals[selected_period]
48-
end
49-
5054
def human_readable thing
5155
case thing
5256
when Period
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
class DrRai::ActionPlansController < AdminController
2+
before_action :authorize_user
3+
before_action :hydrate_plan, only: [:create]
4+
before_action :set_dr_rai_action_plan, only: %i[destroy]
5+
6+
# POST /dr_rai/action_plans or /dr_rai/action_plans.json
7+
def create
8+
@dr_rai_action_plan = DrRai::ActionPlan.new(
9+
statement: dr_rai_action_plan_params[:statement],
10+
actions: dr_rai_action_plan_params[:actions],
11+
dr_rai_indicator: @indicator,
12+
dr_rai_target: @target,
13+
region: @region
14+
)
15+
16+
respond_to do |format|
17+
if @dr_rai_action_plan.save
18+
format.html { redirect_to reports_region_path(report_scope: "facility", id: dr_rai_action_plan_params[:region_slug]) }
19+
else
20+
format.html { render json: @dr_rai_action_plan.errors, status: :unprocessable_entity }
21+
end
22+
end
23+
end
24+
25+
# DELETE /dr_rai/action_plans/1 or /dr_rai/action_plans/1.json
26+
def destroy
27+
@dr_rai_action_plan.discard
28+
29+
respond_to do |format|
30+
format.html { redirect_to reports_region_path(report_scope: "facility", id: @dr_rai_action_plan.region.slug) }
31+
format.json { head :no_content }
32+
end
33+
end
34+
35+
private
36+
37+
# Use callbacks to share common setup or constraints between actions.
38+
def set_dr_rai_action_plan
39+
@dr_rai_action_plan = DrRai::ActionPlan.find(params[:id])
40+
end
41+
42+
# Only allow a list of trusted parameters through.
43+
def dr_rai_action_plan_params
44+
params.require(:dr_rai_action_plan).permit(
45+
:actions,
46+
:indicator_id,
47+
:period,
48+
:region_slug,
49+
:statement,
50+
:target_type,
51+
:target_value
52+
)
53+
end
54+
55+
def authorize_user
56+
authorize { current_admin.accessible_facilities(:view_reports).any? }
57+
end
58+
59+
def hydrate_plan
60+
@region = Region.find_by slug: dr_rai_action_plan_params[:region_slug]
61+
@indicator = DrRai::Indicator.find(dr_rai_action_plan_params[:indicator_id])
62+
period = Period.new(type: :quarter, value: dr_rai_action_plan_params[:period])
63+
@target = DrRai::Target.create!(
64+
type: dr_rai_action_plan_params[:target_type],
65+
period: period,
66+
indicator: @indicator,
67+
numeric_value: dr_rai_action_plan_params[:target_value]
68+
)
69+
end
70+
end

app/controllers/reports/regions_controller.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ def percentage(numerator, denominator)
473473
def quarterly_region_summary(repository, region)
474474
data = repository.schema.send(:region_summaries)
475475
quarterlies = Reports::RegionSummary.group_by(grouping: :quarter, data: data)
476-
quarterlies[region]
476+
cut_off = Period.new(type: :quarter, value: 1.year.ago.to_period.to_quarter_period.value.to_s)
477+
quarterlies[region].select { |k, _| k > cut_off }
477478
end
478479
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Calculatable gets the data for different Dr. Rai Indicators
2+
module DrRai
3+
module Calculatable
4+
def numerator
5+
raise "Unimplemented"
6+
end
7+
8+
def denominator
9+
raise "Unimplemented"
10+
end
11+
end
12+
end

app/models/dr_rai.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module DrRai
2+
def self.table_name_prefix
3+
"dr_rai_"
4+
end
5+
end

app/models/dr_rai/action_plan.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
class DrRai::ActionPlan < ApplicationRecord
2+
include DrRai::Calculatable
3+
4+
belongs_to :dr_rai_indicator, class_name: "DrRai::Indicator"
5+
belongs_to :dr_rai_target, class_name: "DrRai::Target"
6+
belongs_to :region
7+
8+
validates :statement, presence: true
9+
10+
def indicator
11+
dr_rai_indicator
12+
end
13+
14+
def target
15+
dr_rai_target
16+
end
17+
18+
def numerator
19+
indicator.numerator(region)
20+
end
21+
22+
def denominator
23+
target.numeric_value
24+
end
25+
26+
def progress
27+
return 0 unless denominator.positive?
28+
return 100 unless numerator < denominator
29+
30+
(numerator.to_f / denominator * 100).round
31+
end
32+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module DrRai
2+
class BooleanTarget < Target
3+
def achieved_for?(indicator)
4+
completed
5+
end
6+
end
7+
end
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module DrRai
2+
class ContactOverduePatientsIndicator < Indicator
3+
def display_name
4+
"Contact overdue patients"
5+
end
6+
7+
def numerator(region, the_period = period)
8+
numerators(region)[the_period]
9+
end
10+
11+
def denominator(region, the_period = period)
12+
denominators(region)[the_period]
13+
end
14+
15+
def numerators(region)
16+
quarterlies(region).map do |t, data|
17+
[t, data[numerator_key]]
18+
end.to_h
19+
end
20+
21+
def denominators(region)
22+
quarterlies(region).map do |t, data|
23+
[t, data[denominator_key]]
24+
end.to_h
25+
end
26+
27+
def target_type
28+
"DrRai::PercentageTarget"
29+
end
30+
31+
def target_type_frontend
32+
"percent"
33+
end
34+
35+
def numerator_key
36+
"contactable_patients_called"
37+
end
38+
39+
def denominator_key
40+
"overdue_patients"
41+
end
42+
43+
def action
44+
"overdue patients called"
45+
end
46+
end
47+
end

0 commit comments

Comments
 (0)