malawi_hiv_program_reports 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/services/malawi_hiv_program_reports/README.md +16 -0
- data/app/services/malawi_hiv_program_reports/adapters/moh/custom.rb +199 -0
- data/app/services/malawi_hiv_program_reports/archiving_candidates.rb +130 -0
- data/app/services/malawi_hiv_program_reports/arv_refill_periods.rb +311 -0
- data/app/services/malawi_hiv_program_reports/clinic/README.md +5 -0
- data/app/services/malawi_hiv_program_reports/clinic/appointments_report.rb +317 -0
- data/app/services/malawi_hiv_program_reports/clinic/discrepancy_report.rb +42 -0
- data/app/services/malawi_hiv_program_reports/clinic/docs/hypertension_report.md +31 -0
- data/app/services/malawi_hiv_program_reports/clinic/drug_dispensations.rb +48 -0
- data/app/services/malawi_hiv_program_reports/clinic/external_consultation_clients.rb +69 -0
- data/app/services/malawi_hiv_program_reports/clinic/hypertension_report.rb +223 -0
- data/app/services/malawi_hiv_program_reports/clinic/ipt_coverage.rb +112 -0
- data/app/services/malawi_hiv_program_reports/clinic/ipt_report.rb +69 -0
- data/app/services/malawi_hiv_program_reports/clinic/lims_results.rb +55 -0
- data/app/services/malawi_hiv_program_reports/clinic/outcome_list.rb +127 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_alive_and_on_treatment.rb +57 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_due_for_viral_load.rb +39 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_on_antiretrovirals.rb +44 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_on_dtg.rb +36 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_on_treatment.rb +42 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_with_outdated_demographics.rb +173 -0
- data/app/services/malawi_hiv_program_reports/clinic/pregnant_patients.rb +91 -0
- data/app/services/malawi_hiv_program_reports/clinic/regimen_dispensation_data.rb +282 -0
- data/app/services/malawi_hiv_program_reports/clinic/regimen_switch.rb +456 -0
- data/app/services/malawi_hiv_program_reports/clinic/regimens_and_formulations.rb +182 -0
- data/app/services/malawi_hiv_program_reports/clinic/regimens_by_weight_and_gender.rb +108 -0
- data/app/services/malawi_hiv_program_reports/clinic/retention.rb +246 -0
- data/app/services/malawi_hiv_program_reports/clinic/stock_card_report.rb +65 -0
- data/app/services/malawi_hiv_program_reports/clinic/tpt_outcome.rb +494 -0
- data/app/services/malawi_hiv_program_reports/clinic/tx_rtt.rb +169 -0
- data/app/services/malawi_hiv_program_reports/clinic/viral_load.rb +292 -0
- data/app/services/malawi_hiv_program_reports/clinic/viral_load_disaggregated.rb +97 -0
- data/app/services/malawi_hiv_program_reports/clinic/viral_load_results.rb +175 -0
- data/app/services/malawi_hiv_program_reports/clinic/visits_report.rb +113 -0
- data/app/services/malawi_hiv_program_reports/clinic/vl_collection.rb +48 -0
- data/app/services/malawi_hiv_program_reports/cohort/outcomes.rb +338 -0
- data/app/services/malawi_hiv_program_reports/cohort/regimens.rb +69 -0
- data/app/services/malawi_hiv_program_reports/cohort/side_effects.rb +141 -0
- data/app/services/malawi_hiv_program_reports/cohort/tpt.rb +172 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort.rb +278 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_builder.rb +2340 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_disaggregated.rb +608 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_disaggregated_additions.rb +208 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_disaggregated_builder.rb +526 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_struct.rb +219 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_survival_analysis.rb +203 -0
- data/app/services/malawi_hiv_program_reports/moh/moh_tpt.rb +223 -0
- data/app/services/malawi_hiv_program_reports/moh/tpt_newly_initiated.rb +235 -0
- data/app/services/malawi_hiv_program_reports/pepfar/defaulter_list.rb +25 -0
- data/app/services/malawi_hiv_program_reports/pepfar/maternal_status.rb +29 -0
- data/app/services/malawi_hiv_program_reports/pepfar/patient_start_vl.rb +45 -0
- data/app/services/malawi_hiv_program_reports/pepfar/regimen_switch.rb +479 -0
- data/app/services/malawi_hiv_program_reports/pepfar/sc_arvdisp.rb +174 -0
- data/app/services/malawi_hiv_program_reports/pepfar/sc_curr.rb +98 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tb_prev.rb +163 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tb_prev2.rb +222 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tb_prev3.rb +421 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tpt_status.rb +181 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tx_ml.rb +181 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tx_new.rb +202 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tx_rtt.rb +288 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tx_tb.rb +283 -0
- data/app/services/malawi_hiv_program_reports/pepfar/utils.rb +141 -0
- data/app/services/malawi_hiv_program_reports/pepfar/viral_load_coverage.rb +414 -0
- data/app/services/malawi_hiv_program_reports/pepfar/viral_load_coverage2.rb +433 -0
- data/app/services/malawi_hiv_program_reports/report_map.rb +56 -0
- data/app/services/malawi_hiv_program_reports/utils/README.md +8 -0
- data/app/services/malawi_hiv_program_reports/utils/common_sql_query_utils.rb +60 -0
- data/app/services/malawi_hiv_program_reports/utils/concurrency_utils.rb +53 -0
- data/app/services/malawi_hiv_program_reports/utils/docs/common_sql_query_utils.md +53 -0
- data/app/services/malawi_hiv_program_reports/utils/model_utils.rb +66 -0
- data/app/services/malawi_hiv_program_reports/utils/parameter_utils.rb +32 -0
- data/app/services/malawi_hiv_program_reports/utils/time_utils.rb +52 -0
- data/lib/malawi_hiv_program_reports/version.rb +1 -1
- metadata +74 -1
@@ -0,0 +1,283 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MalawiHivProgramReports
|
4
|
+
module Pepfar
|
5
|
+
# TxTb report
|
6
|
+
# rubocop:disable Metrics/ClassLength
|
7
|
+
class TxTb
|
8
|
+
attr_accessor :start_date, :end_date, :report, :rebuild_outcome
|
9
|
+
|
10
|
+
include Utils
|
11
|
+
|
12
|
+
def initialize(start_date:, end_date:, **kwargs)
|
13
|
+
@start_date = start_date.to_date.strftime('%Y-%m-%d 00:00:00')
|
14
|
+
@end_date = end_date.to_date.strftime('%Y-%m-%d 23:59:59')
|
15
|
+
@rebuild_outcome = ActiveModel::Type::Boolean.new.cast(kwargs[:rebuild_outcome]) || false
|
16
|
+
@occupation = kwargs[:occupation]
|
17
|
+
@location = kwargs[:location]
|
18
|
+
end
|
19
|
+
|
20
|
+
def find_report
|
21
|
+
drop_temporary_tables
|
22
|
+
create_temp_earliest_start_date unless temp_eartliest_start_date_exists?
|
23
|
+
init_report
|
24
|
+
build_cohort_tables
|
25
|
+
process_tb_screening
|
26
|
+
process_tb_confirmed_and_on_treatment
|
27
|
+
process_patients_alive_and_on_art
|
28
|
+
process_tb_screened
|
29
|
+
process_tb_confirmed
|
30
|
+
report
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def init_report
|
36
|
+
@report = initialize_report_structure
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize_report_structure
|
40
|
+
pepfar_age_groups.each_with_object({}) do |age_group, report|
|
41
|
+
genders = %i[M F]
|
42
|
+
genders.each do |gender|
|
43
|
+
report[age_group] ||= {}
|
44
|
+
report[age_group][gender] = initialize_gender_metrics
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize_gender_metrics
|
50
|
+
{
|
51
|
+
tx_curr: [],
|
52
|
+
symptom_screen_alone: [],
|
53
|
+
cxr_screen: [],
|
54
|
+
mwrd_screen: [],
|
55
|
+
sceen_pos_new: [],
|
56
|
+
sceen_neg_new: [],
|
57
|
+
started_tb_new: [],
|
58
|
+
sceen_pos_prev: [],
|
59
|
+
sceen_neg_prev: [],
|
60
|
+
started_tb_prev: []
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def drop_temporary_tables
|
65
|
+
execute_action('DROP TABLE IF EXISTS temp_tb_screened;')
|
66
|
+
execute_action('DROP TABLE IF EXISTS temp_tb_confirmed_and_on_treatment;')
|
67
|
+
end
|
68
|
+
|
69
|
+
def build_cohort_tables
|
70
|
+
return unless rebuild_outcome || @occupation.present?
|
71
|
+
|
72
|
+
cohort_builder = MalawiHivProgramReports::Moh::CohortBuilder.new(outcomes_definition: 'pepfar', location: @location)
|
73
|
+
cohort_builder.init_temporary_tables(start_date, end_date, @occupation)
|
74
|
+
end
|
75
|
+
|
76
|
+
def process_tb_screening
|
77
|
+
execute_action(create_temp_tb_screened_query)
|
78
|
+
end
|
79
|
+
|
80
|
+
def create_temp_tb_screened_query
|
81
|
+
<<~SQL
|
82
|
+
CREATE TABLE temp_tb_screened AS
|
83
|
+
SELECT
|
84
|
+
o.person_id as patient_id,
|
85
|
+
LEFT(current_obs.gender, 1) AS gender, MAX(o.obs_datetime) AS screened_date,
|
86
|
+
current_obs.earliest_start_date as enrollment_date,
|
87
|
+
disaggregated_age_group(current_obs.birthdate, DATE('#{end_date.to_date}')) AS age_group,
|
88
|
+
cn.name AS tb_status,
|
89
|
+
GROUP_CONCAT(DISTINCT vcn.name) AS screening_methods
|
90
|
+
FROM obs o
|
91
|
+
INNER JOIN (
|
92
|
+
SELECT o.person_id, MAX(o.obs_datetime) AS obs_datetime, tesd.earliest_start_date, tesd.gender, tesd.birthdate
|
93
|
+
FROM obs o
|
94
|
+
INNER JOIN temp_earliest_start_date tesd ON tesd.patient_id = o.person_id
|
95
|
+
WHERE o.concept_id = #{::ConceptName.find_by_name('TB status').concept_id}
|
96
|
+
AND o.value_coded IN (SELECT concept_id FROM concept_name WHERE name IN ('TB Suspected', 'TB NOT suspected') AND voided = 0)
|
97
|
+
AND o.voided = 0 AND o.obs_datetime BETWEEN '#{start_date}' AND '#{end_date}'
|
98
|
+
GROUP BY o.person_id
|
99
|
+
) current_obs ON current_obs.person_id = o.person_id AND current_obs.obs_datetime = o.obs_datetime
|
100
|
+
INNER JOIN concept_name cn ON cn.concept_id = o.value_coded AND cn.voided = 0
|
101
|
+
LEFT JOIN obs screen_method ON screen_method.concept_id = #{::ConceptName.find_by_name('TB screening method used').concept_id} AND screen_method.voided = 0 AND screen_method.person_id = o.person_id AND DATE(screen_method.obs_datetime) = DATE(current_obs.obs_datetime)
|
102
|
+
LEFT JOIN concept_name vcn ON vcn.concept_id = screen_method.value_coded AND vcn.voided = 0 AND vcn.name IN ('CXR', 'MWRD')
|
103
|
+
WHERE o.concept_id = #{::ConceptName.find_by_name('TB status').concept_id}
|
104
|
+
AND o.voided = 0
|
105
|
+
AND o.value_coded IN (SELECT concept_id FROM concept_name WHERE name IN ('TB Suspected', 'TB NOT suspected') AND voided = 0)
|
106
|
+
AND o.obs_datetime BETWEEN '#{start_date}' AND '#{end_date}'
|
107
|
+
GROUP BY o.person_id
|
108
|
+
SQL
|
109
|
+
end
|
110
|
+
|
111
|
+
def temp_eartliest_start_date_exists?
|
112
|
+
ActiveRecord::Base.connection.table_exists?('temp_earliest_start_date')
|
113
|
+
end
|
114
|
+
|
115
|
+
def create_temp_earliest_start_date
|
116
|
+
cohort_builder = MalawiHivProgramReports::Moh::CohortBuilder.new(outcomes_definition: 'pepfar', location: @location)
|
117
|
+
cohort_builder.init_temporary_tables(start_date, end_date, @occupation)
|
118
|
+
end
|
119
|
+
|
120
|
+
def process_tb_confirmed_and_on_treatment
|
121
|
+
execute_action(create_temp_tb_confirmed_query)
|
122
|
+
end
|
123
|
+
|
124
|
+
def create_temp_tb_confirmed_query
|
125
|
+
<<~SQL
|
126
|
+
CREATE TABLE temp_tb_confirmed_and_on_treatment AS
|
127
|
+
SELECT
|
128
|
+
o.person_id as patient_id,
|
129
|
+
LEFT(p.gender, 1) AS gender,
|
130
|
+
disaggregated_age_group(p.birthdate, DATE('#{end_date.to_date}')) AS age_group,
|
131
|
+
COALESCE(MIN(tcd.value_datetime),MIN(o.obs_datetime)) AS tb_confirmed_date,
|
132
|
+
CASE
|
133
|
+
WHEN COUNT(tcd.value_datetime) > 0 THEN TRUE
|
134
|
+
ELSE FALSE
|
135
|
+
END AS has_tb_confirmed_date,
|
136
|
+
tesd.earliest_start_date as enrollment_date,
|
137
|
+
prev.tb_confirmed_date prev_reading
|
138
|
+
FROM obs o
|
139
|
+
INNER JOIN temp_earliest_start_date tesd ON tesd.patient_id = o.person_id
|
140
|
+
INNER JOIN person p ON p.person_id = o.person_id AND p.voided = 0
|
141
|
+
LEFT JOIN obs tcd ON tcd.concept_id = #{::ConceptName.find_by_name('TB treatment start date').concept_id} AND tcd.voided = 0 AND tcd.person_id = o.person_id
|
142
|
+
LEFT JOIN (
|
143
|
+
SELECT
|
144
|
+
o.person_id,
|
145
|
+
COALESCE(MAX(tcd.value_datetime),MAX(o.obs_datetime)) AS tb_confirmed_date
|
146
|
+
FROM obs o
|
147
|
+
LEFT JOIN obs tcd ON tcd.concept_id = #{::ConceptName.find_by_name('TB treatment start date').concept_id} AND tcd.voided = 0 AND tcd.person_id = o.person_id
|
148
|
+
WHERE o.concept_id = #{::ConceptName.find_by_name('TB status').concept_id}
|
149
|
+
AND o.value_coded = #{::ConceptName.find_by_name('Confirmed TB on treatment').concept_id}
|
150
|
+
AND o.voided = 0
|
151
|
+
AND o.obs_datetime <= '#{start_date}'
|
152
|
+
GROUP BY o.person_id
|
153
|
+
) prev ON prev.person_id = o.person_id
|
154
|
+
WHERE o.concept_id = #{::ConceptName.find_by_name('TB status').concept_id}
|
155
|
+
AND o.value_coded = #{::ConceptName.find_by_name('Confirmed TB on treatment').concept_id}
|
156
|
+
AND o.voided = 0
|
157
|
+
AND o.obs_datetime BETWEEN '#{start_date}' AND '#{end_date}'
|
158
|
+
GROUP BY o.person_id
|
159
|
+
SQL
|
160
|
+
end
|
161
|
+
|
162
|
+
def process_patients_alive_and_on_art
|
163
|
+
find_patients_alive_and_on_art.each do |patient|
|
164
|
+
next unless pepfar_age_groups.include?(patient['age_group'])
|
165
|
+
|
166
|
+
@report[patient['age_group']][patient['gender'].to_sym][:tx_curr] << patient['patient_id']
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def find_patients_alive_and_on_art
|
171
|
+
execute_query(create_patients_alive_and_on_art_query)
|
172
|
+
end
|
173
|
+
|
174
|
+
def create_patients_alive_and_on_art_query
|
175
|
+
<<~SQL
|
176
|
+
SELECT tpo.patient_id, LEFT(tesd.gender, 1) AS gender, disaggregated_age_group(tesd.birthdate, DATE('#{end_date.to_date}')) age_group
|
177
|
+
FROM temp_patient_outcomes tpo
|
178
|
+
INNER JOIN temp_earliest_start_date tesd ON tesd.patient_id = tpo.patient_id
|
179
|
+
WHERE tpo.cum_outcome = 'On antiretrovirals'
|
180
|
+
SQL
|
181
|
+
end
|
182
|
+
|
183
|
+
# rubocop:disable Metrics/AbcSize
|
184
|
+
# rubocop:disable Metrics/MethodLength
|
185
|
+
def process_tb_screened
|
186
|
+
tb_screened_data = find_tb_screened_data
|
187
|
+
tb_screened_data.each do |patient|
|
188
|
+
age_group = patient['age_group']
|
189
|
+
next unless pepfar_age_groups.include?(age_group)
|
190
|
+
|
191
|
+
gender = patient['gender'].to_sym
|
192
|
+
metrics = @report[age_group][gender]
|
193
|
+
enrollment_date = patient['enrollment_date']
|
194
|
+
tb_status = patient['tb_status'].downcase
|
195
|
+
screening_methods = patient['screening_methods']&.split(',')&.map(&:downcase)
|
196
|
+
patient_id = patient['patient_id']
|
197
|
+
process_method(metrics, screening_methods, patient_id)
|
198
|
+
process_screening_results(metrics, enrollment_date, tb_status, patient_id)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def process_tb_confirmed
|
203
|
+
find_tb_confirmed_data.each do |patient|
|
204
|
+
age_group = patient['age_group']
|
205
|
+
next unless pepfar_age_groups.include?(age_group)
|
206
|
+
|
207
|
+
gender = patient['gender'].to_sym
|
208
|
+
metrics = @report[age_group][gender]
|
209
|
+
enrollment_date = patient['enrollment_date']
|
210
|
+
|
211
|
+
if new_on_art(enrollment_date)
|
212
|
+
metrics[:started_tb_new] << patient['patient_id']
|
213
|
+
else
|
214
|
+
metrics[:started_tb_prev] << patient['patient_id']
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def process_screening_results(metrics, enrollment_date, tb_status, patient_id)
|
220
|
+
if new_on_art(enrollment_date)
|
221
|
+
metrics[:sceen_pos_new] << patient_id if ['tb suspected', 'sup', 'confirmed tb not on treatment', 'norx',
|
222
|
+
'confirmed tb on treatment', 'rx'].include?(tb_status)
|
223
|
+
metrics[:sceen_neg_new] << patient_id if ['tb not suspected', 'nosup'].include?(tb_status)
|
224
|
+
else
|
225
|
+
metrics[:sceen_pos_prev] << patient_id if ['tb suspected', 'sup', 'confirmed tb not on treatment',
|
226
|
+
'norx'].include?(tb_status)
|
227
|
+
metrics[:sceen_neg_prev] << patient_id if ['tb not suspected', 'nosup'].include?(tb_status)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def process_method(metrics, methods, patient_id)
|
232
|
+
metrics[:symptom_screen_alone] << patient_id if methods.blank?
|
233
|
+
metrics[:cxr_screen] << patient_id if methods&.include?('cxr') && !methods&.include?('mwrd')
|
234
|
+
metrics[:mwrd_screen] << patient_id if methods&.include?('mwrd')
|
235
|
+
end
|
236
|
+
|
237
|
+
# rubocop:enable Metrics/AbcSize
|
238
|
+
# rubocop:enable Metrics/MethodLength
|
239
|
+
|
240
|
+
def execute_query(query)
|
241
|
+
ActiveRecord::Base.connection.select_all(query)
|
242
|
+
end
|
243
|
+
|
244
|
+
def execute_action(query)
|
245
|
+
ActiveRecord::Base.connection.execute(query)
|
246
|
+
end
|
247
|
+
|
248
|
+
def find_tb_screened_data
|
249
|
+
execute_query(find_tb_screened_data_query)
|
250
|
+
end
|
251
|
+
|
252
|
+
def find_tb_confirmed_data
|
253
|
+
execute_query(find_tb_confirmed_data_query)
|
254
|
+
end
|
255
|
+
|
256
|
+
def find_tb_method_data
|
257
|
+
execute_query(find_tb_method_data_query)
|
258
|
+
end
|
259
|
+
|
260
|
+
def find_tb_screened_data_query
|
261
|
+
<<~SQL
|
262
|
+
SELECT tbs.patient_id, tbs.enrollment_date, LEFT(tbs.gender, 1) AS gender, tbs.age_group, tbs.tb_status, tbs.screened_date, tbs.screening_methods
|
263
|
+
FROM temp_tb_screened tbs
|
264
|
+
SQL
|
265
|
+
end
|
266
|
+
|
267
|
+
def find_tb_confirmed_data_query
|
268
|
+
<<~SQL
|
269
|
+
SELECT t.patient_id, t.gender, t.age_group, t.enrollment_date, t.tb_confirmed_date
|
270
|
+
FROM temp_tb_confirmed_and_on_treatment t
|
271
|
+
WHERE t.tb_confirmed_date > '#{start_date}'
|
272
|
+
AND (t.has_tb_confirmed_date = TRUE OR t.prev_reading IS NULL OR TIMESTAMPDIFF(MONTH,t.prev_reading, t.tb_confirmed_date) > 6)
|
273
|
+
SQL
|
274
|
+
end
|
275
|
+
|
276
|
+
def new_on_art(earliest_start_date)
|
277
|
+
six_months_later = earliest_start_date.to_date + 6.months
|
278
|
+
six_months_later > end_date.to_date
|
279
|
+
end
|
280
|
+
end
|
281
|
+
# rubocop:enable Metrics/ClassLength
|
282
|
+
end
|
283
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MalawiHivProgramReports
|
4
|
+
module Pepfar
|
5
|
+
##
|
6
|
+
# Common utilities for Pepfar reports
|
7
|
+
module Utils
|
8
|
+
##
|
9
|
+
# An array of all groups as required by Pepfar.
|
10
|
+
def pepfar_age_groups
|
11
|
+
@pepfar_age_groups ||= [
|
12
|
+
'Unknown',
|
13
|
+
'<1 year',
|
14
|
+
'1-4 years', '5-9 years',
|
15
|
+
'10-14 years', '15-19 years',
|
16
|
+
'20-24 years',
|
17
|
+
'25-29 years', '30-34 years',
|
18
|
+
'35-39 years', '40-44 years',
|
19
|
+
'45-49 years', '50-54 years',
|
20
|
+
'55-59 years', '60-64 years',
|
21
|
+
'65-69 years', '70-74 years',
|
22
|
+
'75-79 years', '80-84 years',
|
23
|
+
'85-89 years',
|
24
|
+
'90 plus years'
|
25
|
+
].freeze
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Returns the drilldown information for all specified patients (ie patient_ids)
|
30
|
+
#
|
31
|
+
# Information returned for a patient is as follows:
|
32
|
+
# * patient_id
|
33
|
+
# * arv_number or filing_number
|
34
|
+
# * age_group
|
35
|
+
# * birthdate
|
36
|
+
# * gender
|
37
|
+
def pepfar_patient_drilldown_information(patients, current_date)
|
38
|
+
raise ::ArgumentError, "current_date can't be nil" unless current_date
|
39
|
+
|
40
|
+
::Person.joins("LEFT JOIN patient_identifier ON patient_identifier.patient_id = person.person_id
|
41
|
+
AND patient_identifier.voided = 0
|
42
|
+
AND patient_identifier.identifier_type IN (#{pepfar_patient_identifier_type.to_sql})")
|
43
|
+
.where(person_id: patients)
|
44
|
+
.select("person.person_id AS patient_id,
|
45
|
+
person.gender,
|
46
|
+
person.birthdate,
|
47
|
+
disaggregated_age_group(person.birthdate, DATE(#{ActiveRecord::Base.connection.quote(current_date)})) AS age_group,
|
48
|
+
patient_identifier.identifier AS arv_number")
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Returns the preferred Pepfar identifier type.
|
53
|
+
#
|
54
|
+
# In some clinics like Lighthouse Filing numbers are used exclusively and in other
|
55
|
+
# sites, ARV Numbers are used.
|
56
|
+
def pepfar_patient_identifier_type
|
57
|
+
name = GlobalProperty.find_by(property: 'use.filing.numbers')&.property_value
|
58
|
+
name = (name.present? && name == 'true') ? 'Filing number' : 'ARV Number'
|
59
|
+
::PatientIdentifierType.where(name:).select(:patient_identifier_type_id)
|
60
|
+
end
|
61
|
+
|
62
|
+
FULL_6H_COURSE_PILLS = 146
|
63
|
+
FULL_3HP_COURSE_DAYS = 12.days
|
64
|
+
# NOTE: Arrived at 12 days above from how 3HP is prescribed. 1st time prescription
|
65
|
+
# A patient takes 3HP once every week. Therefore it is 4 times a months
|
66
|
+
# Multiply that with 3 months we arrive at 12
|
67
|
+
# Hence the patient is taking this drug 12 times to be considered complete on
|
68
|
+
# 3HP
|
69
|
+
|
70
|
+
##
|
71
|
+
# Returns whether a patient completed their course of TPT
|
72
|
+
def patient_completed_tpt?(patient, tpt)
|
73
|
+
if tpt == '3HP'
|
74
|
+
# return true if patient['total_days_on_medication'].to_i >= 83 # 3 months
|
75
|
+
return true if patient['months_on_tpt'].to_i >= 3
|
76
|
+
|
77
|
+
divider = patient['drug_concepts'].split(',').length > 1 ? 14.0 : 7.0
|
78
|
+
days_on_medication = (patient['total_days_on_medication'] / divider).round
|
79
|
+
days_on_medication.days >= FULL_3HP_COURSE_DAYS
|
80
|
+
else
|
81
|
+
patient['total_days_on_medication'].to_i >= FULL_6H_COURSE_PILLS
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# this just gives all clients who are truly external or drug refill
|
86
|
+
# rubocop:disable Metrics/MethodLength
|
87
|
+
# rubocop:disable Metrics/AbcSize
|
88
|
+
def drug_refills_and_external_consultation_list
|
89
|
+
to_remove = [0]
|
90
|
+
|
91
|
+
type_of_patient_concept = ::ConceptName.find_by_name('Type of patient').concept_id
|
92
|
+
new_patient_concept = ::ConceptName.find_by_name('New patient').concept_id
|
93
|
+
drug_refill_concept = ::ConceptName.find_by_name('Drug refill').concept_id
|
94
|
+
external_concept = ::ConceptName.find_by_name('External consultation').concept_id
|
95
|
+
hiv_clinic_registration_id = ::EncounterType.find_by_name('HIV CLINIC REGISTRATION').encounter_type_id
|
96
|
+
|
97
|
+
ActiveRecord::Base.connection.select_all("
|
98
|
+
SELECT p.person_id patient_id
|
99
|
+
FROM person p
|
100
|
+
INNER JOIN patient_program pp ON pp.patient_id = p.person_id AND pp.program_id = #{::Program.find_by_name('HIV PROGRAM').id} AND pp.voided = 0
|
101
|
+
INNER JOIN patient_state ps ON ps.patient_program_id = pp.patient_program_id AND ps.state = 7 AND ps.start_date IS NOT NULL
|
102
|
+
#{site_manager(operator: 'AND', column: 'pp.site_id', location: @location)}
|
103
|
+
LEFT JOIN encounter as hiv_registration ON hiv_registration.patient_id = p.person_id AND hiv_registration.encounter_datetime < DATE(#{ActiveRecord::Base.connection.quote(end_date)}) AND hiv_registration.encounter_type = #{hiv_clinic_registration_id} AND hiv_registration.voided = 0
|
104
|
+
#{site_manager(operator: 'AND', column: 'hiv_registration.site_id', location: @location)}
|
105
|
+
LEFT JOIN (SELECT * FROM obs WHERE concept_id = #{type_of_patient_concept} AND voided = 0 AND value_coded = #{new_patient_concept} AND obs_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')}
|
106
|
+
#{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}) AS new_patient ON p.person_id = new_patient.person_id
|
107
|
+
LEFT JOIN (SELECT * FROM obs WHERE concept_id = #{type_of_patient_concept} AND voided = 0 AND value_coded = #{drug_refill_concept} AND obs_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')} #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}) AS refill ON p.person_id = refill.person_id
|
108
|
+
LEFT JOIN (SELECT * FROM obs WHERE concept_id = #{type_of_patient_concept} AND voided = 0 AND value_coded = #{external_concept} AND obs_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')} #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}) AS external ON p.person_id = external.person_id
|
109
|
+
WHERE (refill.value_coded IS NOT NULL OR external.value_coded IS NOT NULL)
|
110
|
+
AND NOT (hiv_registration.encounter_id IS NOT NULL OR new_patient.value_coded IS NOT NULL)
|
111
|
+
#{site_manager(operator: 'AND', column: 'p.site_id', location: @location)}
|
112
|
+
GROUP BY p.person_id
|
113
|
+
ORDER BY hiv_registration.encounter_datetime DESC, refill.obs_datetime DESC, external.obs_datetime DESC;").each do |record|
|
114
|
+
to_remove << record['patient_id'].to_i
|
115
|
+
end
|
116
|
+
to_remove.join(',')
|
117
|
+
end
|
118
|
+
# rubocop:enable Metrics/MethodLength
|
119
|
+
# rubocop:enable Metrics/AbcSize
|
120
|
+
|
121
|
+
def rifapentine_concept
|
122
|
+
@rifapentine_concept ||= ::ConceptName.find_by!(name: 'Rifapentine')
|
123
|
+
end
|
124
|
+
|
125
|
+
def isoniazid_rifapentine_concept
|
126
|
+
@isoniazid_rifapentine_concept ||= ::ConceptName.find_by!(name: 'Isoniazid/Rifapentine')
|
127
|
+
end
|
128
|
+
|
129
|
+
def patient_on_3hp?(patient)
|
130
|
+
drug_concepts = patient['drug_concepts'].split(',').collect(&:to_i)
|
131
|
+
drug_concepts.intersect?([rifapentine_concept.concept_id, isoniazid_rifapentine_concept&.concept_id])
|
132
|
+
end
|
133
|
+
|
134
|
+
def patient_on_tb_treatment?(patient_id)
|
135
|
+
::Observation.where(person_id: patient_id, concept_id: ::ConceptName.find_by_name('TB status').concept_id,
|
136
|
+
value_coded: ::ConceptName.find_by_name('Confirmed TB on treatment').concept_id)
|
137
|
+
.where("obs_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')}").exists?
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|