malawi_hiv_program_reports 1.1.16 → 1.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,9 +6,8 @@ module MalawiHivProgramReports
6
6
  # Patients who started TPT just before the start of the current
7
7
  # and have finished within the current reporting period.
8
8
  class TbPrev3
9
+ attr_reader :start_date, :end_date, :check_date, :cut_off_point, :occupation, :location, :report
9
10
 
10
- attr_reader :start_date, :end_date, :check_date, :cut_off_point, :occupation, :location
11
-
12
11
  include MalawiHivProgramReports::Adapters::Moh::Custom
13
12
  include Utils
14
13
  include MalawiHivProgramReports::Utils::CommonSqlQueryUtils
@@ -23,56 +22,160 @@ module MalawiHivProgramReports
23
22
  end
24
23
 
25
24
  def find_report
26
- report = init_report
25
+ process_data
26
+ flatten_the_report
27
+ rescue StandardError => e
28
+ Rails.logger.error("Error generating TB Prev3 report: #{e.message}")
29
+ Rails.logger.error(e.backtrace.join("\n"))
30
+ raise e
31
+ end
32
+
33
+ def fetch_individual_report(patient_id)
34
+ individual_tpt_report(patient_id)
35
+ end
36
+
37
+ private
38
+
39
+ def init_report
40
+ pepfar_age_groups.each_with_object({}) do |age_group, report|
41
+ report[age_group] = %w[Male Female].each_with_object({}) do |gender, gender_sub_report|
42
+ gender_sub_report[gender] = %w[6H 3HP].each_with_object({}) do |tpt, tpt_sub_report|
43
+ tpt_sub_report[tpt] = initialize_tpt_metrics
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def process_data
50
+ @report = init_report
51
+ addittional_groups
27
52
  patients = group_patients_by_tpt_course(patients_on_tpt)
28
53
 
29
- load_patients_into_report(report, patients.six_h, '6H') do |patient|
54
+ load_patients_into_report(patients.six_h, '6H') do |patient|
30
55
  # 6H has a constant dosage of 1 pill per day
31
56
  patient_completed_tpt?(patient, '6H')
32
57
  end
33
58
 
34
- load_patients_into_report(report, patients.three_hp, '3HP') do |patient|
59
+ load_patients_into_report(patients.three_hp, '3HP') do |patient|
35
60
  # 3HP daily dosages vary by patient weight can't use easily use pills
36
61
  # to determine course completion
37
62
  patient_completed_tpt?(patient, '3HP')
38
63
  end
39
-
40
- report
41
64
  end
42
65
 
43
- def fetch_individual_report(patient_id)
44
- individual_tpt_report(patient_id)
66
+ def initialize_tpt_metrics
67
+ {
68
+ started_new_on_art: [],
69
+ started_previously_on_art: [],
70
+ completed_new_on_art: [],
71
+ completed_previously_on_art: []
72
+ }
45
73
  end
46
74
 
47
- private
48
-
49
- def init_report
50
- pepfar_age_groups.each_with_object({}) do |age_group, report|
51
- report[age_group] = %w[M F Unknown].each_with_object({}) do |gender, gender_sub_report|
52
- gender_sub_report[gender] = %w[6H 3HP].each_with_object({}) do |tpt, tpt_sub_report|
53
- tpt_sub_report[tpt] = {
54
- started_new_on_art: [],
55
- started_previously_on_art: [],
56
- completed_new_on_art: [],
57
- completed_previously_on_art: []
58
- }
59
- end
75
+ def addittional_groups
76
+ @report['All'] = {}
77
+ %w[Male FP FNP FBf].each do |key|
78
+ @report['All'][key] = %w[6H 3HP].each_with_object({}) do |tpt, tpt_sub_report|
79
+ tpt_sub_report[tpt] = initialize_tpt_metrics
60
80
  end
61
81
  end
62
82
  end
63
83
 
64
- def load_patients_into_report(report, patients, tpt, &patient_has_completed_tpt)
84
+ def load_patients_into_report(patients, tpt, &patient_has_completed_tpt)
65
85
  patients.each do |patient|
66
86
  next if patient['transfer_in'] == 1 && !patient_has_completed_tpt[patient]
67
87
 
68
88
  age_group = patient['age_group']
69
- gender = patient['gender']&.first&.upcase || 'Unknown'
89
+ gender = patient['gender']
70
90
  tpt_states = find_patient_tpt_state(patient, &patient_has_completed_tpt)
71
91
 
72
92
  tpt_states.each do |tpt_state|
73
- report[age_group][gender][tpt][tpt_state] << patient
93
+ add_to_report(age_group, gender, tpt, tpt_state, patient)
94
+ end
95
+ end
96
+ end
97
+
98
+ def flatten_the_report
99
+ result = []
100
+ @report.each do |age_group, genders|
101
+ genders.each do |gender, tpts|
102
+ tpts.each do |tpt, states|
103
+ result << process_age_group_report(age_group, gender, tpt, states)
104
+ end
105
+ end
106
+ end
107
+ combined = combine_entries(result)
108
+ sort_flattened_report(combined)
109
+ end
110
+
111
+ def combine_entries(flattened_report)
112
+ ## currently we will have two entries of age_group and gender
113
+ ## we need to combine them into one
114
+ ## example [{age_group: '20-25 years', gender: 'Female', 3hp_started_new_on_art: [10] ...}, {age_group: '20-25 years', gender: 'Female', 6hp_started_new_on_art: [10] ...}]
115
+ ## should be combined to [{age_group: '20-25 years', gender: 'Female', 3hp_started_new_on_art: [10], 6hp_started_new_on_art: [10] ...}]
116
+
117
+ result = {}
118
+
119
+ flattened_report.each do |entry|
120
+ age_group = entry[:age_group]
121
+ gender = entry[:gender]
122
+ key = "#{age_group}_#{gender}"
123
+
124
+ if result.key?(key)
125
+ entry.each do |k, v|
126
+ next if %i[age_group gender].include?(k)
127
+
128
+ result[key][k] ||= []
129
+ result[key][k] += v
130
+ end
131
+ else
132
+ result[key] = entry.dup
74
133
  end
75
134
  end
135
+
136
+ result.values
137
+ end
138
+
139
+ def sort_flattened_report(result)
140
+ new_group = pepfar_age_groups.map { |age_group| age_group }
141
+ new_group << 'All'
142
+ gender_scores = { 'Female' => 0, 'Male' => 1, 'FNP' => 3, 'FP' => 2, 'FBf' => 4 }
143
+ result_scores = result.sort_by do |item|
144
+ gender_score = gender_scores[item[:gender]] || 999
145
+ age_group_score = new_group.index(item[:age_group]) || 999
146
+ [gender_score, age_group_score]
147
+ end
148
+ # remove all unknown age groups
149
+ result_scores.reject { |item| item[:age_group].match?(/unknown/i) }
150
+ end
151
+
152
+ def process_age_group_report(age_group, gender, tpt, states)
153
+ {
154
+ age_group:,
155
+ gender:,
156
+ "#{tpt}_started_new_on_art": states[:started_new_on_art],
157
+ "#{tpt}_started_previously_on_art": states[:started_previously_on_art],
158
+ "#{tpt}_completed_new_on_art": states[:completed_new_on_art],
159
+ "#{tpt}_completed_previously_on_art": states[:completed_previously_on_art]
160
+ }
161
+ end
162
+
163
+ def add_to_report(age_group, gender, tpt, tpt_state, patient)
164
+ @report[age_group][gender][tpt][tpt_state] << patient['patient_id']
165
+ if gender == 'Male'
166
+ report['All'][gender][tpt][tpt_state] << patient['patient_id']
167
+ return
168
+ end
169
+
170
+ maternal_status = patient['maternal_status']
171
+ case maternal_status
172
+ when 'FP'
173
+ @report['All']['FP'][tpt][tpt_state] << patient['patient_id']
174
+ when 'FBf'
175
+ @report['All']['FBf'][tpt][tpt_state] << patient['patient_id']
176
+ else
177
+ @report['All']['FNP'][tpt][tpt_state] << patient['patient_id']
178
+ end
76
179
  end
77
180
 
78
181
  def find_patient_tpt_state(patient, &patient_has_completed_tpt)
@@ -121,97 +224,42 @@ module MalawiHivProgramReports
121
224
 
122
225
  def fetch_patients_on_tpt
123
226
  ActiveRecord::Base.connection.select_all <<~SQL
124
- SELECT person.person_id AS patient_id,
125
- patient_identifier.identifier AS arv_number,
227
+ SELECT denominator_patient.patient_id,
126
228
  DATE(MIN(orders.start_date)) AS tpt_initiation_date,
127
- #{function_manager(function: 'date_antiretrovirals_started', location: @location, args: "person.person_id::int, MIN(denominator_patient.start_date)::date, #{@location}::int")} AS art_start_date,
128
- #{function_manager(function: 'patient_outcome', location: @location, args: "person.person_id::int, #{@end_date}::date, #{@location}::int")} AS outcome,
129
- person.gender,
130
- person.birthdate,
131
- #{function_manager(function: 'disaggregated_age_group', location: @location, args: "person.birthdate::date, #{@end_date}::date")} AS age_group
132
- FROM person #{current_partition}
133
- LEFT JOIN patient_identifier #{current_partition}
134
- ON patient_identifier.patient_id = person.person_id
135
- AND patient_identifier.voided = 0
136
- AND #{in_manager(column: 'patient_identifier.identifier_type', values: "(SELECT patient_identifier_type_id FROM patient_identifier_type #{current_partition} WHERE name = 'ARV Number')")}
137
- #{site_manager(operator: 'AND', column: 'patient_identifier.site_id', location: @location)}
138
- LEFT JOIN (#{current_occupation_query}) AS current_occupation ON current_occupation.person_id = person.person_id
139
- #{site_manager(operator: 'AND', column: 'current_occupation.site_id', location: @location)}
140
- INNER JOIN(
141
- SELECT denominator_encounter.patient_id AS patient_id, patient_state.start_date AS start_date
142
- FROM person #{current_partition}
143
- INNER JOIN patient_program #{current_partition}
144
- ON patient_program.patient_id = person.person_id
145
- AND #{in_manager(column: 'patient_program.program_id', values: "(SELECT program_id FROM program #{current_partition} WHERE name = 'HIV PROGRAM')")}
146
- AND patient_program.voided = 0
147
- INNER JOIN patient_state #{current_partition}
148
- ON patient_state.patient_program_id = patient_program.patient_program_id
149
- AND patient_state.state = 7 /* State: 7 == On antiretrovirals */
150
- AND patient_state.start_date < DATE(#{start_date})
151
- AND patient_state.voided = 0
152
- INNER JOIN encounter #{current_partition} AS denominator_encounter
153
- ON denominator_encounter.patient_id = patient_program.patient_id
154
- AND #{in_manager(column: 'denominator_encounter.program_id', values: "(SELECT program_id FROM program #{current_partition} WHERE name = 'HIV PROGRAM')")}
155
- AND #{in_manager(column: 'denominator_encounter.encounter_type', values: "(SELECT encounter_type_id FROM encounter_type WHERE name = 'Treatment')")}
156
- AND denominator_encounter.encounter_datetime >= #{interval_manager(date: start_date, value: 6, interval: 'MONTH', operator: '-')}
157
- AND denominator_encounter.encounter_datetime <= DATE(#{start_date})
158
- AND denominator_encounter.voided = 0
159
- #{site_manager(operator: 'AND', column: 'patient_program.site_id', location: @location)}
160
- GROUP BY denominator_encounter.patient_id, patient_state.start_date
161
- ) AS denominator_patient ON denominator_patient.patient_id = person.person_id
229
+ denominator_patient.earliest_start_date AS art_start_date,
230
+ outcome.cum_outcome,
231
+ CASE denominator_patient.gender
232
+ WHEN 'M' THEN 'Male'
233
+ WHEN 'F' THEN 'Female'
234
+ ELSE 'Unknown'
235
+ END gender,
236
+ c.maternal_status,
237
+ denominator_patient.birthdate,
238
+ disaggregated_age_group(denominator_patient.birthdate, DATE(#{end_date})) AS age_group
239
+ FROM cdr_temp_cohort_members #{current_partition} denominator_patient
240
+ LEFT JOIN cdr_temp_maternal_status #{current_partition} c ON c.patient_id = denominator_patient.patient_id
241
+ INNER JOIN cdr_temp_patient_outcomes #{current_partition} outcome ON denominator_patient.patient_id = outcome.patient_id
162
242
  INNER JOIN encounter #{current_partition} AS prescription_encounter
163
243
  ON prescription_encounter.patient_id = denominator_patient.patient_id
164
- AND prescription_encounter.program_id = 1
165
- AND #{in_manager(column: 'prescription_encounter.encounter_type', values: "(SELECT encounter_type_id FROM encounter_type WHERE name = 'Treatment')")}
166
- AND prescription_encounter.encounter_datetime >= #{interval_manager(date: start_date, value: 6, interval: 'MONTH', operator: '-')}
244
+ AND prescription_encounter.program_id = 1 /* HIV Program */
245
+ AND prescription_encounter.encounter_type = (SELECT encounter_type_id FROM encounter_type WHERE name = 'Treatment' LIMIT 1)
246
+ AND prescription_encounter.encounter_datetime >= DATE(#{start_date}) - INTERVAL 6 MONTH
167
247
  AND prescription_encounter.encounter_datetime <= DATE(#{end_date})
168
248
  AND prescription_encounter.voided = 0
169
- INNER JOIN orders #{current_partition}
249
+ INNER JOIN orders #{current_partition} AS orders
170
250
  ON orders.encounter_id = prescription_encounter.encounter_id
171
- AND #{in_manager(column: 'orders.order_type_id', values: "(SELECT order_type_id FROM order_type #{current_partition} WHERE name = 'Drug order')")}
172
- AND orders.start_date >= #{interval_manager(date: start_date, value: 6, interval: 'MONTH', operator: '-')}
173
- AND orders.start_date <= CAST(#{end_date} as DATE)
251
+ AND orders.order_type_id = (SELECT order_type_id FROM order_type WHERE name = 'Drug order' LIMIT 1)
252
+ AND orders.start_date >= DATE(#{start_date}) - INTERVAL 6 MONTH
253
+ AND orders.start_date <= DATE(#{end_date})
174
254
  AND orders.voided = 0
175
- INNER JOIN concept_name #{current_partition}
255
+ INNER JOIN concept_name
176
256
  ON concept_name.concept_id = orders.concept_id
177
- AND #{in_manager(column: 'concept_name.name', values: "'Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine'" )}
178
- INNER JOIN drug_order #{current_partition}
257
+ AND concept_name.name IN ('Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine')
258
+ INNER JOIN drug_order #{current_partition} AS drug_order
179
259
  ON drug_order.order_id = orders.order_id
180
260
  AND drug_order.quantity > 0
181
- WHERE person.voided = 0 #{%w[Military Civilian].include?(@occupation) ? 'AND' : ''} #{occupation_filter(occupation: @occupation, field_name: 'value', table_name: 'current_occupation', include_clause: false)}
182
- #{site_manager(operator: 'AND', column: 'person.site_id', location: @location)}
183
- AND person.person_id NOT IN (
184
- /* External consultations */
185
- SELECT DISTINCT registration_encounter.patient_id
186
- FROM patient_program #{current_partition} pp
187
- INNER JOIN program #{current_partition} p ON p.program_id = pp.program_id AND p.name = 'HIV PROGRAM' AND p.retired = 0
188
- INNER JOIN encounter #{current_partition} AS registration_encounter
189
- ON registration_encounter.patient_id = pp.patient_id
190
- AND registration_encounter.program_id = pp.program_id
191
- AND registration_encounter.encounter_datetime < #{interval_manager(date: end_date, value: 1, interval: 'MONTH', operator: '+')}
192
- AND registration_encounter.voided = 0
193
- INNER JOIN (
194
- SELECT MAX(encounter.encounter_datetime) AS encounter_datetime, encounter.patient_id
195
- FROM encounter #{current_partition}
196
- INNER JOIN encounter_type
197
- ON encounter_type.encounter_type_id = encounter.encounter_type
198
- AND encounter_type.name = 'Registration'
199
- INNER JOIN program #{current_partition}
200
- ON program.program_id = encounter.program_id
201
- AND program.name = 'HIV PROGRAM'
202
- WHERE encounter.encounter_datetime < CAST(#{end_date} AS DATE) AND encounter.voided = 0
203
- GROUP BY encounter.patient_id
204
- ) AS max_registration_encounter
205
- ON max_registration_encounter.patient_id = registration_encounter.patient_id
206
- AND max_registration_encounter.encounter_datetime = registration_encounter.encounter_datetime
207
- INNER JOIN obs #{current_partition} AS patient_type_obs
208
- ON patient_type_obs.encounter_id = registration_encounter.encounter_id
209
- AND #{in_manager(column: 'patient_type_obs.concept_id', values: "(SELECT concept_id FROM concept_name #{current_partition} WHERE name = 'Type of patient' AND voided = 0)")}
210
- AND #{in_manager(column: 'patient_type_obs.value_coded', values: "(SELECT concept_id FROM concept_name #{current_partition} WHERE name IN ('Drug refill', 'External consultation') AND voided = 0)")}
211
- AND patient_type_obs.voided = 0
212
- WHERE pp.voided = 0
213
- )
214
- GROUP BY person.person_id #{group_by_columns('patient_identifier.identifier, person.gender, person.birthdate, denominator_patient.start_date')}
261
+ WHERE denominator_patient.date_enrolled < DATE(#{start_date}) AND denominator_patient.gender IN ('M', 'F')
262
+ GROUP BY denominator_patient.patient_id
215
263
  SQL
216
264
  end
217
265
 
@@ -220,41 +268,39 @@ module MalawiHivProgramReports
220
268
  c_start_date = ActiveRecord::Base.connection.quote(result[:start_date])
221
269
  c_end_date = ActiveRecord::Base.connection.quote(client_tpt_end_date(patient_id, c_start_date))
222
270
  ActiveRecord::Base.connection.select_one <<-SQL
223
- SELECT
224
- CASE
225
- WHEN tpt_transfer_in_obs.value_datetime IS NULL THEN DATE(MIN(o.start_date))
226
- WHEN tpt_transfer_in_obs.value_datetime > MIN(o.start_date) THEN DATE(MIN(o.start_date))
227
- ELSE DATE(tpt_transfer_in_obs.value_datetime)
228
- END AS tpt_initiation_date,
229
- COUNT(DISTINCT(DATE(o.start_date))) AS months_on_tpt,
230
- SUM(dor.quantity) + SUM(CASE WHEN tpt_transfer_in_obs.value_numeric IS NOT NULL THEN tpt_transfer_in_obs.value_numeric ELSE 0 END) AS total_pills_taken,
231
- SUM(DATEDIFF(o.auto_expire_date, o.start_date)) + SUM(CASE WHEN tpt_transfer_in_obs.value_datetime IS NOT NULL THEN DATEDIFF(tpt_transfer_in_obs.obs_datetime, tpt_transfer_in_obs.value_datetime) ElSE 0 END) AS total_days_on_medication,
232
- GROUP_CONCAT(DISTINCT o.concept_id SEPARATOR ',') AS drug_concepts,
233
- CASE
234
- WHEN tpt_transfer_in_obs.value_numeric IS NOT NULL THEN 1
235
- ELSE 0
236
- END AS transfer_in,
237
- MAX(o.start_date) AS last_dispensed_date,
238
- MAX(o.auto_expire_date) AS auto_expire_date
239
- FROM orders #{current_partition} o
240
- INNER JOIN concept_name #{current_partition} cn
241
- ON cn.concept_id = o.concept_id
242
- AND cn.name IN ('Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine')
243
- #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
244
- LEFT JOIN obs #{current_partition} tpt_transfer_in_obs
245
- ON tpt_transfer_in_obs.person_id = o.patient_id
246
- AND tpt_transfer_in_obs.concept_id = #{::ConceptName.find_by_name('TPT Drugs Received').concept_id}
247
- AND tpt_transfer_in_obs.voided = 0
248
- #{site_manager(operator: 'AND', column: 'tpt_transfer_in_obs.site_id', location: @location)}
249
- AND tpt_transfer_in_obs.value_drug IN (SELECT drug_id FROM drug #{current_partition} WHERE concept_id IN (SELECT concept_id FROM concept_name #{current_partition} WHERE name IN ('Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine')))
250
- INNER JOIN drug_order #{current_partition} dor
251
- ON dor.order_id = o.order_id
252
- AND dor.quantity > 0
253
- WHERE DATE(o.start_date) BETWEEN DATE(#{c_start_date}) AND DATE(#{c_end_date})
254
- AND o.order_type_id IN (SELECT order_type_id FROM order_type #{current_partition} WHERE name = 'Drug order')
255
- AND o.voided = 0
256
- AND o.patient_id = #{patient_id}
257
- GROUP BY o.patient_id
271
+ SELECT
272
+ CASE
273
+ WHEN tpt_transfer_in_obs.value_datetime IS NULL THEN DATE(MIN(o.start_date))
274
+ WHEN tpt_transfer_in_obs.value_datetime > MIN(o.start_date) THEN DATE(MIN(o.start_date))
275
+ ELSE DATE(tpt_transfer_in_obs.value_datetime)
276
+ END AS tpt_initiation_date,
277
+ COUNT(DISTINCT(DATE(o.start_date))) AS months_on_tpt,
278
+ SUM(dor.quantity) + SUM(CASE WHEN tpt_transfer_in_obs.value_numeric IS NOT NULL THEN tpt_transfer_in_obs.value_numeric ELSE 0 END) AS total_pills_taken,
279
+ SUM(DATEDIFF(o.auto_expire_date, o.start_date)) + SUM(CASE WHEN tpt_transfer_in_obs.value_datetime IS NOT NULL THEN DATEDIFF(tpt_transfer_in_obs.obs_datetime, tpt_transfer_in_obs.value_datetime) ElSE 0 END) AS total_days_on_medication,
280
+ GROUP_CONCAT(DISTINCT o.concept_id SEPARATOR ',') AS drug_concepts,
281
+ CASE
282
+ WHEN tpt_transfer_in_obs.value_numeric IS NOT NULL THEN 1
283
+ ELSE 0
284
+ END AS transfer_in,
285
+ MAX(o.start_date) AS last_dispensed_date,
286
+ MAX(o.auto_expire_date) AS auto_expire_date
287
+ FROM orders #{current_partition} o
288
+ INNER JOIN concept_name cn
289
+ ON cn.concept_id = o.concept_id
290
+ AND cn.name IN ('Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine')
291
+ LEFT JOIN obs #{current_partition} tpt_transfer_in_obs
292
+ ON tpt_transfer_in_obs.person_id = o.patient_id
293
+ AND tpt_transfer_in_obs.concept_id = #{ConceptName.find_by_name('TPT Drugs Received').concept_id}
294
+ AND tpt_transfer_in_obs.voided = 0
295
+ AND tpt_transfer_in_obs.value_drug IN (SELECT drug_id FROM drug WHERE concept_id IN (SELECT concept_id FROM concept_name WHERE name IN ('Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine')))
296
+ INNER JOIN drug_order #{current_partition} dor
297
+ ON dor.order_id = o.order_id
298
+ AND dor.quantity > 0
299
+ WHERE DATE(o.start_date) BETWEEN DATE(#{c_start_date}) AND DATE(#{c_end_date})
300
+ AND o.order_type_id IN (SELECT order_type_id FROM order_type WHERE name = 'Drug order')
301
+ AND o.voided = 0
302
+ AND o.patient_id = #{patient_id}
303
+ GROUP BY o.patient_id
258
304
  SQL
259
305
  end
260
306
 
@@ -292,13 +338,12 @@ module MalawiHivProgramReports
292
338
  ELSE '6H'
293
339
  END AS course
294
340
  FROM obs #{current_partition} o
295
- WHERE o.concept_id = #{::ConceptName.find_by_name('TPT Drugs Received').concept_id}
296
- AND o.voided = 0
297
- #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
298
- AND o.value_drug IN (SELECT drug_id FROM drug #{current_partition} WHERE concept_id IN (SELECT concept_id FROM concept_name #{current_partition} WHERE name IN ('Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine')))
299
- AND o.person_id = #{patient_id}
300
- AND o.value_numeric IS NOT NULL
301
- AND DATE(o.obs_datetime) <= DATE(#{start_date})
341
+ WHERE o.concept_id = #{ConceptName.find_by_name('TPT Drugs Received').concept_id}
342
+ AND o.voided = 0
343
+ AND o.value_drug IN (SELECT drug_id FROM drug WHERE concept_id IN (SELECT concept_id FROM concept_name WHERE name IN ('Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine')))
344
+ AND o.person_id = #{patient_id}
345
+ AND o.value_numeric IS NOT NULL
346
+ AND DATE(o.obs_datetime) <= DATE(#{start_date})
302
347
  GROUP BY DATE(o.obs_datetime)
303
348
  ORDER BY DATE(o.obs_datetime) DESC
304
349
  )
@@ -313,15 +358,14 @@ module MalawiHivProgramReports
313
358
  ELSE '6H'
314
359
  END AS course
315
360
  FROM orders #{current_partition} o
316
- INNER JOIN encounter #{current_partition} e ON e.encounter_id = o.encounter_id AND e.voided = 0 AND e.program_id = 1 /* HIV PROGRAM */
361
+ INNER JOIN encounter #{current_partition} e ON e.encounter_id = o.encounter_id AND e.voided = 0 AND e.program_id = 1 /* HIV Program */
317
362
  INNER JOIN drug_order #{current_partition} dor ON dor.order_id = o.order_id AND dor.quantity > 0
318
- WHERE o.order_type_id IN (SELECT order_type_id FROM order_type #{current_partition} WHERE name = 'Drug order')
319
- AND o.voided = 0
320
- #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
321
- AND o.concept_id IN (#{::ConceptName.where(name: ['Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine']).select(:concept_id).to_sql})
322
- AND o.patient_id = #{patient_id}
323
- AND o.auto_expire_date IS NOT NULL
324
- AND DATE(o.start_date) <= DATE(#{start_date})
363
+ WHERE o.order_type_id = (SELECT order_type_id FROM order_type WHERE name = 'Drug order' LIMIT 1)
364
+ AND o.voided = 0
365
+ AND o.concept_id IN (#{ConceptName.where(name: ['Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine']).select(:concept_id).to_sql})
366
+ AND o.patient_id = #{patient_id}
367
+ AND o.auto_expire_date IS NOT NULL
368
+ AND DATE(o.start_date) <= DATE(#{start_date})
325
369
  GROUP BY DATE(o.start_date)
326
370
  ORDER BY DATE(o.start_date) DESC
327
371
  )
@@ -341,13 +385,12 @@ module MalawiHivProgramReports
341
385
  ELSE '6H'
342
386
  END AS course
343
387
  FROM obs #{current_partition} o
344
- WHERE o.concept_id = #{::ConceptName.find_by_name('TPT Drugs Received').concept_id}
345
- AND o.voided = 0
346
- #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
347
- AND o.value_drug IN (SELECT drug_id FROM drug #{current_partition} WHERE concept_id IN (SELECT concept_id FROM concept_name #{current_partition} WHERE name IN ('Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine')))
348
- AND o.person_id = #{patient_id}
349
- AND o.value_numeric IS NOT NULL
350
- AND DATE(o.obs_datetime) BETWEEN DATE(#{start_date}) AND DATE(#{end_date})
388
+ WHERE o.concept_id = #{ConceptName.find_by_name('TPT Drugs Received').concept_id}
389
+ AND o.voided = 0
390
+ AND o.value_drug IN (SELECT drug_id FROM drug WHERE concept_id IN (SELECT concept_id FROM concept_name WHERE name IN ('Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine')))
391
+ AND o.person_id = #{patient_id}
392
+ AND o.value_numeric IS NOT NULL
393
+ AND DATE(o.obs_datetime) BETWEEN DATE(#{start_date}) AND DATE(#{end_date})
351
394
  GROUP BY DATE(o.obs_datetime)
352
395
  ORDER BY DATE(o.obs_datetime) DESC
353
396
  )
@@ -362,15 +405,14 @@ module MalawiHivProgramReports
362
405
  ELSE '6H'
363
406
  END AS course
364
407
  FROM orders #{current_partition} o
365
- INNER JOIN encounter #{current_partition} e ON e.encounter_id = o.encounter_id AND e.voided = 0 AND e.program_id = 1 /* HIV PROGRAM */
408
+ INNER JOIN encounter #{current_partition} e ON e.encounter_id = o.encounter_id AND e.voided = 0 AND e.program_id = 1 /* HIV Program */
366
409
  INNER JOIN drug_order #{current_partition} dor ON dor.order_id = o.order_id AND dor.quantity > 0
367
- WHERE o.order_type_id IN (SELECT order_type_id FROM order_type #{current_partition} WHERE name = 'Drug order')
368
- AND o.voided = 0
369
- #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
370
- AND o.concept_id IN (#{::ConceptName.where(name: ['Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine']).select(:concept_id).to_sql})
371
- AND o.patient_id = #{patient_id}
372
- AND o.auto_expire_date IS NOT NULL
373
- AND DATE(o.start_date) BETWEEN DATE(#{start_date}) AND DATE(#{end_date})
410
+ WHERE o.order_type_id = (SELECT order_type_id FROM order_type WHERE name = 'Drug order' LIMIT 1)
411
+ AND o.voided = 0
412
+ AND o.concept_id IN (#{ConceptName.where(name: ['Rifapentine', 'Isoniazid', 'Isoniazid/Rifapentine']).select(:concept_id).to_sql})
413
+ AND o.patient_id = #{patient_id}
414
+ AND o.auto_expire_date IS NOT NULL
415
+ AND DATE(o.start_date) BETWEEN DATE(#{start_date}) AND DATE(#{end_date})
374
416
  GROUP BY DATE(o.start_date)
375
417
  ORDER BY DATE(o.start_date) DESC
376
418
  )
@@ -414,7 +456,7 @@ module MalawiHivProgramReports
414
456
  end
415
457
 
416
458
  def isoniazid_rifapentine_drug
417
- @isoniazid_rifapentine_drug ||= ::Drug.find_by!(concept_id: isoniazid_rifapentine_concept.concept_id)
459
+ @isoniazid_rifapentine_drug ||= Drug.find_by!(concept_id: isoniazid_rifapentine_concept.concept_id)
418
460
  end
419
461
  end
420
462
  end
@@ -20,6 +20,7 @@ module MalawiHivProgramReports
20
20
  raise 'Location is required' if @location.blank?
21
21
 
22
22
  @report = init_report
23
+ addittional_groups
23
24
  end
24
25
 
25
26
  def find_report
@@ -33,20 +34,31 @@ module MalawiHivProgramReports
33
34
 
34
35
  private
35
36
 
36
- GENDER = %w[Male Female Unknown].freeze
37
+ GENDER = %w[Male Female].freeze
37
38
 
38
39
  def init_report
39
40
  pepfar_age_groups.each_with_object({}) do |age_group, report|
40
41
  report[age_group] = GENDER.each_with_object({}) do |gender, age_group_report|
41
- age_group_report[gender] = {
42
- less_than_three_months: [],
43
- three_to_five_months: [],
44
- greater_than_six_months: []
45
- }
42
+ age_group_report[gender] = initialize_gender_metrics
46
43
  end
47
44
  end
48
45
  end
49
46
 
47
+ def addittional_groups
48
+ @report['All'] = {}
49
+ %w[Male FP FNP FBf].each do |key|
50
+ @report['All'][key] = initialize_gender_metrics
51
+ end
52
+ end
53
+
54
+ def initialize_gender_metrics
55
+ {
56
+ less_than_three_months: [],
57
+ three_to_five_months: [],
58
+ greater_than_six_months: []
59
+ }
60
+ end
61
+
50
62
  def process_age_group_report(age_group, gender, age_group_report)
51
63
  {
52
64
  age_group:,
@@ -60,7 +72,11 @@ module MalawiHivProgramReports
60
72
  def flatten_the_report
61
73
  result = []
62
74
  report.each do |age_group, age_group_report|
75
+ next if age_group == 'Unknown'
76
+
63
77
  age_group_report.each_key do |gender|
78
+ next if gender == 'Unknown' # skip unknown
79
+
64
80
  result << process_age_group_report(age_group, gender, age_group_report[gender])
65
81
  end
66
82
  end
@@ -77,24 +93,7 @@ module MalawiHivProgramReports
77
93
  result_scores.reject { |item| item[:age_group].match?(/unknown/i) }
78
94
  end
79
95
 
80
- def process_data
81
- patients = ActiveRecord::Base.connection.select_all <<~SQL
82
- SELECT tesd.patient_id,
83
- CASE tesd.gender
84
- WHEN 'M' THEN 'Male'
85
- WHEN 'F' THEN 'Female'
86
- ELSE 'Unknown'
87
- END gender,
88
- disaggregated_age_group(tesd.birthdate, '#{end_date}') age_group,
89
- TIMESTAMPDIFF(DAY, tcm.start_date, tcm.auto_expire_date) prescribed_days
90
- FROM cdr_temp_cohort_members #{current_partition} tesd#{' '}
91
- INNER JOIN cdr_temp_patient_outcomes #{current_partition} tpo ON tpo.patient_id = tesd.patient_id AND tpo.cum_outcome = 'On antiretrovirals'
92
- INNER JOIN cdr_temp_min_auto_expire_date #{current_partition} tcm ON tcm.patient_id = tesd.patient_id
93
- WHERE tesd.date_enrolled <= '#{end_date}'
94
- SQL
95
-
96
- return report if patients.blank?
97
-
96
+ def process_indicators(patients)
98
97
  patients.each do |patient|
99
98
  prescribe_days = patient['prescribed_days'].to_i
100
99
  age_group = patient['age_group']
@@ -110,8 +109,47 @@ module MalawiHivProgramReports
110
109
 
111
110
  report[age_group][gender][indicator.to_sym] << patient['patient_id']
112
111
  end
112
+ end
113
+
114
+ def aggregation(age_group, gender, indicator, patient)
115
+ report[age_group][gender][indicator.to_sym] << patient['patient_id']
116
+ if gender == 'Male'
117
+ report['All'][gender][indicator.to_sym] << patient['patient_id']
118
+ return
119
+ end
120
+
121
+ maternal_status = patient['maternal_status']
122
+ case maternal_status
123
+ when 'FP'
124
+ @report['All']['FP'][indicator.to_sym] << patient['patient_id']
125
+ when 'FBf'
126
+ @report['All']['FBf'][indicator.to_sym] << patient['patient_id']
127
+ else
128
+ @report['All']['FNP'][indicator.to_sym] << patient['patient_id']
129
+ end
130
+ end
131
+
132
+ def process_data
133
+ patients = ActiveRecord::Base.connection.select_all <<~SQL
134
+ SELECT tesd.patient_id,
135
+ CASE tesd.gender
136
+ WHEN 'M' THEN 'Male'
137
+ WHEN 'F' THEN 'Female'
138
+ ELSE 'Unknown'
139
+ END gender,
140
+ c.maternal_status,
141
+ disaggregated_age_group(tesd.birthdate, '#{end_date}') age_group,
142
+ TIMESTAMPDIFF(DAY, tcm.start_date, tcm.auto_expire_date) prescribed_days
143
+ FROM cdr_temp_cohort_members #{current_partition} tesd
144
+ INNER JOIN cdr_temp_patient_outcomes #{current_partition} tpo ON tpo.patient_id = tesd.patient_id AND tpo.cum_outcome = 'On antiretrovirals'
145
+ INNER JOIN cdr_temp_min_auto_expire_date #{current_partition} tcm ON tcm.patient_id = tesd.patient_id
146
+ LEFT JOIN cdr_temp_maternal_status #{current_partition} c ON c.patient_id = tesd.patient_id
147
+ WHERE tesd.date_enrolled <= '#{end_date}' AND tesd.gender IN ('M', 'F')
148
+ SQL
149
+
150
+ return report if patients.blank?
113
151
 
114
- report
152
+ process_indicators(patients)
115
153
  end
116
154
  end
117
155
  end