malawi_hiv_program_reports 1.0.1 → 1.0.2

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/app/services/malawi_hiv_program_reports/README.md +16 -0
  3. data/app/services/malawi_hiv_program_reports/adapters/moh/custom.rb +199 -0
  4. data/app/services/malawi_hiv_program_reports/archiving_candidates.rb +130 -0
  5. data/app/services/malawi_hiv_program_reports/arv_refill_periods.rb +311 -0
  6. data/app/services/malawi_hiv_program_reports/clinic/README.md +5 -0
  7. data/app/services/malawi_hiv_program_reports/clinic/appointments_report.rb +317 -0
  8. data/app/services/malawi_hiv_program_reports/clinic/discrepancy_report.rb +42 -0
  9. data/app/services/malawi_hiv_program_reports/clinic/docs/hypertension_report.md +31 -0
  10. data/app/services/malawi_hiv_program_reports/clinic/drug_dispensations.rb +48 -0
  11. data/app/services/malawi_hiv_program_reports/clinic/external_consultation_clients.rb +69 -0
  12. data/app/services/malawi_hiv_program_reports/clinic/hypertension_report.rb +223 -0
  13. data/app/services/malawi_hiv_program_reports/clinic/ipt_coverage.rb +112 -0
  14. data/app/services/malawi_hiv_program_reports/clinic/ipt_report.rb +69 -0
  15. data/app/services/malawi_hiv_program_reports/clinic/lims_results.rb +55 -0
  16. data/app/services/malawi_hiv_program_reports/clinic/outcome_list.rb +127 -0
  17. data/app/services/malawi_hiv_program_reports/clinic/patients_alive_and_on_treatment.rb +57 -0
  18. data/app/services/malawi_hiv_program_reports/clinic/patients_due_for_viral_load.rb +39 -0
  19. data/app/services/malawi_hiv_program_reports/clinic/patients_on_antiretrovirals.rb +44 -0
  20. data/app/services/malawi_hiv_program_reports/clinic/patients_on_dtg.rb +36 -0
  21. data/app/services/malawi_hiv_program_reports/clinic/patients_on_treatment.rb +42 -0
  22. data/app/services/malawi_hiv_program_reports/clinic/patients_with_outdated_demographics.rb +173 -0
  23. data/app/services/malawi_hiv_program_reports/clinic/pregnant_patients.rb +91 -0
  24. data/app/services/malawi_hiv_program_reports/clinic/regimen_dispensation_data.rb +282 -0
  25. data/app/services/malawi_hiv_program_reports/clinic/regimen_switch.rb +456 -0
  26. data/app/services/malawi_hiv_program_reports/clinic/regimens_and_formulations.rb +182 -0
  27. data/app/services/malawi_hiv_program_reports/clinic/regimens_by_weight_and_gender.rb +108 -0
  28. data/app/services/malawi_hiv_program_reports/clinic/retention.rb +246 -0
  29. data/app/services/malawi_hiv_program_reports/clinic/stock_card_report.rb +65 -0
  30. data/app/services/malawi_hiv_program_reports/clinic/tpt_outcome.rb +494 -0
  31. data/app/services/malawi_hiv_program_reports/clinic/tx_rtt.rb +169 -0
  32. data/app/services/malawi_hiv_program_reports/clinic/viral_load.rb +292 -0
  33. data/app/services/malawi_hiv_program_reports/clinic/viral_load_disaggregated.rb +97 -0
  34. data/app/services/malawi_hiv_program_reports/clinic/viral_load_results.rb +175 -0
  35. data/app/services/malawi_hiv_program_reports/clinic/visits_report.rb +113 -0
  36. data/app/services/malawi_hiv_program_reports/clinic/vl_collection.rb +48 -0
  37. data/app/services/malawi_hiv_program_reports/cohort/outcomes.rb +338 -0
  38. data/app/services/malawi_hiv_program_reports/cohort/regimens.rb +69 -0
  39. data/app/services/malawi_hiv_program_reports/cohort/side_effects.rb +141 -0
  40. data/app/services/malawi_hiv_program_reports/cohort/tpt.rb +172 -0
  41. data/app/services/malawi_hiv_program_reports/moh/cohort.rb +278 -0
  42. data/app/services/malawi_hiv_program_reports/moh/cohort_builder.rb +2340 -0
  43. data/app/services/malawi_hiv_program_reports/moh/cohort_disaggregated.rb +608 -0
  44. data/app/services/malawi_hiv_program_reports/moh/cohort_disaggregated_additions.rb +208 -0
  45. data/app/services/malawi_hiv_program_reports/moh/cohort_disaggregated_builder.rb +526 -0
  46. data/app/services/malawi_hiv_program_reports/moh/cohort_struct.rb +219 -0
  47. data/app/services/malawi_hiv_program_reports/moh/cohort_survival_analysis.rb +203 -0
  48. data/app/services/malawi_hiv_program_reports/moh/moh_tpt.rb +223 -0
  49. data/app/services/malawi_hiv_program_reports/moh/tpt_newly_initiated.rb +235 -0
  50. data/app/services/malawi_hiv_program_reports/pepfar/defaulter_list.rb +25 -0
  51. data/app/services/malawi_hiv_program_reports/pepfar/maternal_status.rb +29 -0
  52. data/app/services/malawi_hiv_program_reports/pepfar/patient_start_vl.rb +45 -0
  53. data/app/services/malawi_hiv_program_reports/pepfar/regimen_switch.rb +479 -0
  54. data/app/services/malawi_hiv_program_reports/pepfar/sc_arvdisp.rb +174 -0
  55. data/app/services/malawi_hiv_program_reports/pepfar/sc_curr.rb +98 -0
  56. data/app/services/malawi_hiv_program_reports/pepfar/tb_prev.rb +163 -0
  57. data/app/services/malawi_hiv_program_reports/pepfar/tb_prev2.rb +222 -0
  58. data/app/services/malawi_hiv_program_reports/pepfar/tb_prev3.rb +421 -0
  59. data/app/services/malawi_hiv_program_reports/pepfar/tpt_status.rb +181 -0
  60. data/app/services/malawi_hiv_program_reports/pepfar/tx_ml.rb +181 -0
  61. data/app/services/malawi_hiv_program_reports/pepfar/tx_new.rb +202 -0
  62. data/app/services/malawi_hiv_program_reports/pepfar/tx_rtt.rb +288 -0
  63. data/app/services/malawi_hiv_program_reports/pepfar/tx_tb.rb +283 -0
  64. data/app/services/malawi_hiv_program_reports/pepfar/utils.rb +141 -0
  65. data/app/services/malawi_hiv_program_reports/pepfar/viral_load_coverage.rb +414 -0
  66. data/app/services/malawi_hiv_program_reports/pepfar/viral_load_coverage2.rb +433 -0
  67. data/app/services/malawi_hiv_program_reports/report_map.rb +56 -0
  68. data/app/services/malawi_hiv_program_reports/utils/README.md +8 -0
  69. data/app/services/malawi_hiv_program_reports/utils/common_sql_query_utils.rb +60 -0
  70. data/app/services/malawi_hiv_program_reports/utils/concurrency_utils.rb +53 -0
  71. data/app/services/malawi_hiv_program_reports/utils/docs/common_sql_query_utils.md +53 -0
  72. data/app/services/malawi_hiv_program_reports/utils/model_utils.rb +66 -0
  73. data/app/services/malawi_hiv_program_reports/utils/parameter_utils.rb +32 -0
  74. data/app/services/malawi_hiv_program_reports/utils/time_utils.rb +52 -0
  75. data/lib/malawi_hiv_program_reports/version.rb +1 -1
  76. metadata +74 -1
@@ -0,0 +1,608 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MalawiHivProgramReports
4
+ module Moh
5
+ # This class is used to add additional cohort disaggregated data
6
+ # rubocop:disable Metrics/ClassLength
7
+ class CohortDisaggregated
8
+ include MalawiHivProgramReports::Utils::ModelUtils
9
+ include MalawiHivProgramReports::Adapters::Moh::Custom
10
+
11
+ def initialize(name:, type:, start_date:, end_date:, rebuild:, **kwargs)
12
+ @name = name
13
+ @type = type
14
+ @start_date = start_date
15
+ @end_date = end_date
16
+ @rebuild = rebuild
17
+ @occupation = kwargs[:occupation]
18
+ @location = kwargs[:location]
19
+ @adapter = ActiveRecord::Base.connection.adapter_name.downcase
20
+ end
21
+
22
+ def find_report
23
+ build_report
24
+ end
25
+
26
+ def build_report
27
+ builder = CohortDisaggregatedBuilder.new(outcomes_definition: 'moh', location: @location,
28
+ occupation: @occupation)
29
+ builder.build(nil, @start_date, @end_date)
30
+ end
31
+
32
+ def initialize_disaggregated
33
+ ActiveRecord::Base.connection.execute('DROP TABLE IF EXISTS temp_disaggregated')
34
+
35
+ ActiveRecord::Base.connection.execute(
36
+ 'CREATE TABLE IF NOT EXISTS temp_disaggregated (
37
+ patient_id INTEGER NOT NULL,
38
+ age_group VARCHAR(20),
39
+ initial_maternal_status VARCHAR(10),
40
+ maternal_status VARCHAR(10),
41
+ given_ipt INT(1),
42
+ screened_for_tb INT(1),
43
+ site_id INT NOT NULL DEFAULT 1,
44
+ PRIMARY KEY(patient_id, site_id)
45
+ );'
46
+ )
47
+
48
+ { temp_disaggregated: 'created' }
49
+ end
50
+
51
+ def disaggregated(quarter, age_group)
52
+ if quarter == 'pepfar'
53
+ start_date = @start_date
54
+ end_date = @end_date
55
+
56
+ begin
57
+ records = ActiveRecord::Base.connection.select_one("SELECT count(*) rec_count FROM temp_patient_outcomes #{site_manager(
58
+ operator: 'AND', column: 'site_id', location: @location
59
+ )}")
60
+ @rebuild = true if records['rec_count'].to_i < 1
61
+ rescue StandardError => e
62
+ Rails.logger.error "Error: #{e.message}"
63
+ initialize_disaggregated
64
+ rebuild_outcomes 'pepfar'
65
+ end
66
+
67
+ if @rebuild
68
+ initialize_disaggregated
69
+ rebuild_outcomes 'pepfar'
70
+ end
71
+
72
+ else
73
+ start_date, end_date = generate_start_date_and_end_date(quarter)
74
+
75
+ if @rebuild
76
+ initialize_disaggregated
77
+ art_service = CohortBuilder.new
78
+ art_service.init_temporary_tables(@start_date, @end_date, @occupation)
79
+ art_service.update_tb_status(end_date)
80
+ end
81
+ end
82
+
83
+ tmp = get_age_groups(age_group, start_date, end_date)
84
+
85
+ # A hack to get female that were pregnant / breastfeeding at the beginning of the reporting period + those are currently the same state
86
+ if age_group == 'Pregnant'
87
+ tmp_arr = []
88
+ (tmp || []).each do |data|
89
+ begin
90
+ date_enrolled = data['date_enrolled'].to_date
91
+ rescue StandardError => e
92
+ Rails.logger.error "Error: #{e.message}"
93
+ raise data.inspect
94
+ end
95
+ earliest_start_date = begin
96
+ data['earliest_start_date']
97
+ rescue StandardError
98
+ date_enrolled
99
+ end
100
+
101
+ imstaus = data['initial_maternal_status']
102
+ mstatus = data['mstatus']
103
+
104
+ if (date_enrolled >= start_date && date_enrolled <= end_date) && imstaus == 'FP' && (date_enrolled == earliest_start_date)
105
+ tmp_arr << data
106
+ elsif mstatus == 'FP'
107
+ tmp_arr << data
108
+ end
109
+ end
110
+
111
+ tmp = tmp_arr
112
+ end
113
+
114
+ if age_group == 'Breastfeeding'
115
+ tmp_arr = []
116
+ (tmp || []).each do |data|
117
+ begin
118
+ date_enrolled = data['date_enrolled'].to_date
119
+ rescue StandardError
120
+ raise data.inspect
121
+ end
122
+ earliest_start_date = begin
123
+ data['earliest_start_date']
124
+ rescue StandardError
125
+ date_enrolled
126
+ end
127
+
128
+ imstaus = data['initial_maternal_status']
129
+ mstatus = data['mstatus']
130
+
131
+ if (date_enrolled >= start_date && date_enrolled <= end_date) && imstaus == 'FBf' && (date_enrolled == earliest_start_date)
132
+ tmp_arr << data
133
+ elsif mstatus == 'FBf'
134
+ tmp_arr << data
135
+ end
136
+ end
137
+
138
+ tmp = tmp_arr
139
+ end
140
+ # ........................... Hack ends .......... Will clean up later
141
+
142
+ on_art = []
143
+ all_clients = []
144
+ all_clients_outcomes = {}
145
+
146
+ (tmp || []).each do |pat|
147
+ patient_id = pat['patient_id'].to_i
148
+ outcome = pat['outcome']
149
+
150
+ on_art << patient_id if outcome == 'On antiretrovirals'
151
+ all_clients << patient_id
152
+ all_clients_outcomes[patient_id] = outcome
153
+ end
154
+
155
+ list = {}
156
+
157
+ if all_clients.blank? && %w[Breastfeeding Pregnant].include?(age_group)
158
+ list[age_group] = {}
159
+ list[age_group]['F'] = {
160
+ tx_new: [], tx_curr: [],
161
+ tx_screened_for_tb: [],
162
+ tx_given_ipt: []
163
+ }
164
+ return list
165
+ elsif all_clients.blank?
166
+ return {}
167
+ end
168
+
169
+ big_insert tmp, age_group if /year|month/i.match?(age_group)
170
+
171
+ (tmp || []).each do |r|
172
+ gender = r['gender']&.first || 'Unknown'
173
+ patient_id = r['patient_id'].to_i
174
+ tx_new, tx_curr, tx_given_ipt, tx_screened_for_tb = get_numbers(r, age_group, start_date, end_date,
175
+ all_clients_outcomes)
176
+
177
+ list[age_group] = {} if list[age_group].blank?
178
+
179
+ if list[age_group][gender].blank?
180
+ list[age_group][gender] = {
181
+ tx_new: [], tx_curr: [],
182
+ tx_screened_for_tb: [],
183
+ tx_given_ipt: []
184
+ }
185
+ end
186
+
187
+ list[age_group][gender][:tx_new] << r['patient_id'] if tx_new
188
+ list[age_group][gender][:tx_curr] << r['patient_id'] if tx_curr
189
+ list[age_group][gender][:tx_given_ipt] << r['patient_id'] if tx_given_ipt
190
+ list[age_group][gender][:tx_screened_for_tb] << r['patient_id'] if tx_screened_for_tb
191
+
192
+ date_enrolled = r['date_enrolled'].to_date
193
+
194
+ if gender == 'F' && all_clients_outcomes[patient_id] == 'On antiretrovirals'
195
+ insert_female_maternal_status(patient_id, age_group, end_date)
196
+ elsif gender == 'F' && (date_enrolled >= start_date && date_enrolled <= end_date)
197
+ insert_female_maternal_status(patient_id, age_group, end_date)
198
+ end
199
+ end
200
+
201
+ list
202
+ end
203
+
204
+ def generate_start_date_and_end_date(quarter)
205
+ return [@start_date, @end_date] if quarter == 'Custom'
206
+
207
+ quarter, quarter_year = quarter.humanize.split
208
+
209
+ quarter_start_dates = [
210
+ "#{quarter_year}-01-01".to_date,
211
+ "#{quarter_year}-04-01".to_date,
212
+ "#{quarter_year}-07-01".to_date,
213
+ "#{quarter_year}-10-01".to_date
214
+ ]
215
+
216
+ quarter_end_dates = [
217
+ "#{quarter_year}-03-31".to_date,
218
+ "#{quarter_year}-06-30".to_date,
219
+ "#{quarter_year}-09-30".to_date,
220
+ "#{quarter_year}-12-31".to_date
221
+ ]
222
+
223
+ current_quarter = (quarter.match(/\d+/).to_s.to_i - 1)
224
+ quarter_beginning = quarter_start_dates[current_quarter]
225
+ quarter_ending = quarter_end_dates[current_quarter]
226
+
227
+ [quarter_beginning, quarter_ending]
228
+ end
229
+
230
+ def screened_for_tb(my_patient_id, age_group, start_date, end_date)
231
+ data = ActiveRecord::Base.connection.select_one <<~SQL
232
+ SELECT patient_screened_for_tb(#{my_patient_id},
233
+ '#{start_date.to_date}', '#{end_date.to_date}', #{@location}) AS screened;
234
+ SQL
235
+
236
+ screened = data['screened'].to_i
237
+
238
+ ActiveRecord::Base.connection.execute <<~SQL
239
+ UPDATE temp_disaggregated SET screened_for_tb = #{screened},
240
+ age_group = '#{age_group}'
241
+ WHERE patient_id = #{my_patient_id} AND site_id = #{@location};
242
+ SQL
243
+
244
+ screened
245
+ end
246
+
247
+ def given_ipt(my_patient_id, age_group, start_date, end_date)
248
+ data = ActiveRecord::Base.connection.select_one <<~SQL
249
+ SELECT patient_given_ipt(#{my_patient_id},
250
+ '#{start_date.to_date}', '#{end_date.to_date}', #{@location}) AS given;
251
+ SQL
252
+
253
+ given = data['given'].to_i
254
+
255
+ ActiveRecord::Base.connection.execute <<~SQL
256
+ UPDATE temp_disaggregated SET given_ipt = #{given} ,
257
+ age_group = '#{age_group}'
258
+ WHERE patient_id = #{my_patient_id} AND site_id = #{@location};
259
+ SQL
260
+
261
+ given
262
+ end
263
+
264
+ def get_numbers(data, age_group, start_date, end_date, outcomes)
265
+ patient_id = data['patient_id'].to_i
266
+ tx_new = false
267
+ tx_curr = false
268
+ tx_screened_for_tb = false
269
+ tx_given_ipt = false
270
+ outcome = outcomes[patient_id]
271
+
272
+ begin
273
+ date_enrolled = data['date_enrolled'].to_date
274
+ rescue StandardError
275
+ raise data.inspect
276
+ end
277
+ earliest_start_date = begin
278
+ data['earliest_start_date'].to_date
279
+ rescue StandardError
280
+ nil
281
+ end
282
+
283
+ if date_enrolled >= start_date && date_enrolled <= end_date
284
+ tx_new = true if !earliest_start_date.blank? && (date_enrolled == earliest_start_date)
285
+
286
+ tx_curr = true if outcome == 'On antiretrovirals'
287
+ elsif outcome == 'On antiretrovirals'
288
+ tx_curr = true
289
+ end
290
+
291
+ if age_group == 'Pregnant'
292
+ tx_new = false if data['initial_maternal_status'] != 'FP' && tx_new
293
+
294
+ tx_curr = false if data['mstatus'] != 'FP'
295
+ end
296
+
297
+ if age_group == 'Breastfeeding'
298
+ tx_new = false if data['initial_maternal_status'] != 'FBf' && tx_new
299
+
300
+ tx_curr = false if data['mstatus'] != 'FBf'
301
+ end
302
+
303
+ [tx_new, tx_curr, tx_given_ipt, tx_screened_for_tb]
304
+ end
305
+
306
+ def get_age_groups(age_group, _start_date, _end_date)
307
+ if age_group != 'Pregnant' && age_group != 'FNP' && age_group != 'Not pregnant' && age_group != 'Breastfeeding'
308
+
309
+ age_group_patients = ActiveRecord::Base.connection.select_all <<~SQL
310
+ SELECT
311
+ patient_id, `disaggregated_age_group`(date(birthdate), date('#{@end_date}')) AS age_group
312
+ FROM temp_earliest_start_date e #{site_manager(operator: 'WHERE', column: 'e.site_id', location: @location)}
313
+ GROUP BY e.patient_id
314
+ HAVING #{@adapter == 'mysql2' ? 'age_group' : "`disaggregated_age_group`(date(birthdate), date('#{@end_date}'))"} = '#{age_group}';
315
+ SQL
316
+ age_group_patient_ids = [0]
317
+ (age_group_patients || []).each do |patient|
318
+ age_group_patient_ids << patient['patient_id'].to_i
319
+ end
320
+
321
+ results = ActiveRecord::Base.connection.select_all <<~SQL
322
+ SELECT o.cum_outcome AS outcome, e.*
323
+ FROM temp_earliest_start_date e
324
+ LEFT JOIN temp_patient_outcomes o ON o.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
325
+ WHERE date_enrolled <= '#{@end_date}' #{site_manager(operator: 'AND', column: 'e.site_id', location: @location)}
326
+ AND e.patient_id IN(#{age_group_patient_ids.join(',')})
327
+ GROUP BY #{@adapter == 'mysql2' ? 'e.patient_id' : 'o.cum_outcome, e.*'};
328
+ SQL
329
+ elsif age_group == 'Pregnant'
330
+ create_mysql_female_maternal_status
331
+ results = ActiveRecord::Base.connection.select_all <<~SQL
332
+ SELECT
333
+ e.*, maternal_status AS mstatus,
334
+ t2.initial_maternal_status,
335
+ t3.cum_outcome AS outcome
336
+ FROM temp_earliest_start_date e
337
+ INNER JOIN temp_disaggregated t2 ON t2.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 't2.site_id', location: @location)}
338
+ INNER JOIN temp_patient_outcomes t3 ON t3.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 't3.site_id', location: @location)}
339
+ WHERE maternal_status = 'FP' OR initial_maternal_status = 'FP' #{site_manager(operator: 'AND', column: 'e.site_id', location: @location)}
340
+ GROUP BY #{@adapter == 'mysql2' ? 'e.patient_id' : 't3.cum_outcome, maternal_status, t2.initial_maternal_status, e.*'};
341
+ SQL
342
+
343
+ elsif age_group == 'Breastfeeding'
344
+ create_mysql_female_maternal_status
345
+ results = ActiveRecord::Base.connection.select_all <<~SQL
346
+ SELECT
347
+ e.*, maternal_status AS mstatus,
348
+ initial_maternal_status,
349
+ t3.cum_outcome AS outcome
350
+ FROM temp_earliest_start_date e
351
+ INNER JOIN temp_disaggregated t2 ON t2.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 't2.site_id', location: @location)}
352
+ INNER JOIN temp_patient_outcomes t3 ON t3.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 't3.site_id', location: @location)}
353
+ WHERE maternal_status = 'FBf' OR initial_maternal_status = 'FBf' #{site_manager(operator: 'AND', column: 'e.site_id', location: @location)}
354
+ GROUP BY #{@adapter == 'mysql2' ? 'e.patient_id' : 't3.cum_outcome, maternal_status, initial_maternal_status, e.*'};
355
+ SQL
356
+
357
+ elsif age_group == 'FNP'
358
+ create_mysql_female_maternal_status
359
+ results = ActiveRecord::Base.connection.select_all <<~SQL
360
+ SELECT
361
+ e.*, maternal_status AS mstatus,
362
+ initial_maternal_status,
363
+ t3.cum_outcome AS outcome
364
+ FROM temp_earliest_start_date e
365
+ INNER JOIN temp_disaggregated t2 ON t2.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 't2.site_id', location: @location)}
366
+ INNER JOIN temp_patient_outcomes t3 ON t3.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 't3.site_id', location: @location)}
367
+ WHERE maternal_status = 'FNP' #{site_manager(operator: 'AND', column: 'e.site_id', location: @location)}
368
+ GROUP BY #{@adapter == 'mysql2' ? 'e.patient_id' : 't3.cum_outcome, maternal_status, initial_maternal_status, e.*'};
369
+ SQL
370
+
371
+ end
372
+
373
+ results
374
+ end
375
+
376
+ def create_mysql_female_maternal_status
377
+ ActiveRecord::Base.connection.execute <<~SQL
378
+ DROP FUNCTION IF EXISTS female_maternal_status;
379
+ SQL
380
+
381
+ ActiveRecord::Base.connection.execute <<~SQL
382
+ CREATE FUNCTION female_maternal_status(my_patient_id int, end_datetime datetime, my_site_id int) RETURNS VARCHAR(20)
383
+ DETERMINISTIC
384
+ BEGIN
385
+
386
+ DECLARE breastfeeding_date DATETIME;
387
+ DECLARE pregnant_date DATETIME;
388
+ DECLARE maternal_status VARCHAR(20);
389
+ DECLARE obs_value_coded INT(11);
390
+
391
+
392
+ SET @reason_for_starting = (SELECT concept_id FROM concept_name WHERE LOWER(name) = LOWER('Reason for ART eligibility') LIMIT 1);
393
+
394
+ SET @pregnant_concepts := (SELECT GROUP_CONCAT(concept_id) FROM concept_name WHERE LOWER(name) IN (LOWER('Is patient pregnant?'), LOWER('Patient pregnant')));
395
+ SET @breastfeeding_concept := (SELECT GROUP_CONCAT(concept_id) FROM concept_name WHERE LOWER(name) = 'Breastfeeding');
396
+
397
+ SET pregnant_date = (SELECT MAX(obs_datetime) FROM obs WHERE concept_id IN (@pregnant_concepts) AND voided = 0 AND person_id = my_patient_id AND obs_datetime <= end_datetime AND site_id = my_site_id);
398
+ SET breastfeeding_date = (SELECT MAX(obs_datetime) FROM obs WHERE concept_id IN (@breastfeeding_concept) AND voided = 0 AND person_id = my_patient_id AND obs_datetime <= end_datetime AND site_id = my_site_id);
399
+
400
+ IF pregnant_date IS NULL THEN
401
+ SET pregnant_date = (SELECT MAX(obs_datetime) FROM obs WHERE concept_id = @reason_for_starting AND voided = 0 AND person_id = my_patient_id AND obs_datetime <= end_datetime AND value_coded IN(1755) AND site_id = my_site_id);
402
+ END IF;
403
+
404
+ IF breastfeeding_date IS NULL THEN
405
+ SET breastfeeding_date = (SELECT MAX(obs_datetime) FROM obs WHERE concept_id = @reason_for_starting AND voided = 0 AND person_id = my_patient_id AND obs_datetime <= end_datetime AND value_coded IN(834,5632) AND site_id = my_site_id);
406
+ END IF;
407
+
408
+ IF pregnant_date IS NULL AND breastfeeding_date IS NULL THEN SET maternal_status = "FNP";
409
+ ELSEIF pregnant_date IS NOT NULL AND breastfeeding_date IS NOT NULL THEN SET maternal_status = "Unknown";
410
+ ELSEIF pregnant_date IS NULL AND breastfeeding_date IS NOT NULL THEN SET maternal_status = "Check BF";
411
+ ELSEIF pregnant_date IS NOT NULL AND breastfeeding_date IS NULL THEN SET maternal_status = "Check FP";
412
+ END IF;
413
+
414
+ IF maternal_status = 'Unknown' THEN
415
+
416
+ IF breastfeeding_date <= pregnant_date THEN
417
+ SET obs_value_coded = (SELECT value_coded FROM obs WHERE concept_id IN(@pregnant_concepts) AND voided = 0 AND person_id = my_patient_id AND obs_datetime = pregnant_date AND site_id = my_site_id LIMIT 1);
418
+ IF obs_value_coded = 1065 THEN SET maternal_status = 'FP';
419
+ ELSEIF obs_value_coded = 1066 THEN SET maternal_status = 'FNP';
420
+ END IF;
421
+ END IF;
422
+
423
+ IF breastfeeding_date > pregnant_date THEN
424
+ SET obs_value_coded = (SELECT value_coded FROM obs WHERE concept_id IN(@breastfeeding_concept) AND voided = 0 AND person_id = my_patient_id AND obs_datetime = breastfeeding_date AND site_id = my_site_id LIMIT 1);
425
+ IF obs_value_coded = 1065 THEN SET maternal_status = 'FBf';
426
+ ELSEIF obs_value_coded = 1066 THEN SET maternal_status = 'FNP';
427
+ END IF;
428
+ END IF;
429
+
430
+ IF DATE(breastfeeding_date) = DATE(pregnant_date) AND maternal_status = 'FNP' THEN
431
+ SET obs_value_coded = (SELECT value_coded FROM obs WHERE concept_id IN(@breastfeeding_concept) AND voided = 0 AND person_id = my_patient_id AND obs_datetime = breastfeeding_date AND site_id = my_site_id LIMIT 1);
432
+ IF obs_value_coded = 1065 THEN SET maternal_status = 'FBf';
433
+ ELSEIF obs_value_coded = 1066 THEN SET maternal_status = 'FNP';
434
+ END IF;
435
+ END IF;
436
+ END IF;
437
+
438
+ IF maternal_status = 'Check FP' THEN
439
+
440
+ SET obs_value_coded = (SELECT value_coded FROM obs WHERE concept_id IN(@pregnant_concepts) AND voided = 0 AND person_id = my_patient_id AND obs_datetime = pregnant_date AND site_id = my_site_id LIMIT 1);
441
+ IF obs_value_coded = 1065 THEN SET maternal_status = 'FP';
442
+ ELSEIF obs_value_coded = 1066 THEN SET maternal_status = 'FNP';
443
+ END IF;
444
+
445
+ IF obs_value_coded IS NULL THEN
446
+ SET obs_value_coded = (SELECT GROUP_CONCAT(value_coded) FROM obs WHERE concept_id IN(7563) AND voided = 0 AND person_id = my_patient_id AND obs_datetime = pregnant_date AND site_id = my_site_id);
447
+ IF obs_value_coded IN(1755) THEN SET maternal_status = 'FP';
448
+ END IF;
449
+ END IF;
450
+
451
+ IF maternal_status = 'Check FP' THEN SET maternal_status = 'FNP';
452
+ END IF;
453
+ END IF;
454
+
455
+ IF maternal_status = 'Check BF' THEN
456
+
457
+ SET obs_value_coded = (SELECT value_coded FROM obs WHERE concept_id IN(@breastfeeding_concept) AND voided = 0 AND person_id = my_patient_id AND obs_datetime = breastfeeding_date AND site_id = my_site_id LIMIT 1);
458
+ IF obs_value_coded = 1065 THEN SET maternal_status = 'FBf';
459
+ ELSEIF obs_value_coded = 1066 THEN SET maternal_status = 'FNP';
460
+ END IF;
461
+
462
+ IF obs_value_coded IS NULL THEN
463
+ SET obs_value_coded = (SELECT GROUP_CONCAT(value_coded) FROM obs WHERE concept_id IN(7563) AND voided = 0 AND person_id = my_patient_id AND obs_datetime = breastfeeding_date AND site_id = my_site_id);
464
+ IF obs_value_coded IN(834,5632) THEN SET maternal_status = 'FBf';
465
+ END IF;
466
+ END IF;
467
+
468
+ IF maternal_status = 'Check BF' THEN SET maternal_status = 'FNP';
469
+ END IF;
470
+ END IF;
471
+
472
+
473
+
474
+ RETURN maternal_status;
475
+ END;
476
+ SQL
477
+ end
478
+
479
+ def rebuild_outcomes(report_type)
480
+ MalawiHivProgramReports::Moh::CohortBuilder.new(outcomes_definition: report_type, location: @location).init_temporary_tables(@start_date,
481
+ @end_date, @occupation)
482
+ end
483
+
484
+ def insert_female_maternal_status(patient_id, age_group, end_date)
485
+ encounter_types = []
486
+ encounter_types << encounter_type('HIV CLINIC CONSULTATION').encounter_type_id
487
+ encounter_types << encounter_type('HIV STAGING').encounter_type_id
488
+
489
+ pregnant_concepts = []
490
+ pregnant_concepts << concept_name('Is patient pregnant?').concept_id
491
+ pregnant_concepts << concept_name('patient pregnant').concept_id
492
+
493
+ results = ActiveRecord::Base.connection.select_all <<~SQL
494
+ SELECT person_id, obs.value_coded value_coded
495
+ FROM obs obs
496
+ INNER JOIN encounter enc ON enc.encounter_id = obs.encounter_id AND enc.encounter_type IN(#{encounter_types.join(',')}) AND enc.voided = 0 AND enc.program_id = 1 #{site_manager(operator: 'AND', column: 'enc.site_id', location: @location)}
497
+ WHERE obs.person_id = #{patient_id}
498
+ AND obs.obs_datetime <= '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
499
+ AND obs.concept_id IN(#{pregnant_concepts.join(',')})
500
+ AND obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
501
+ AND DATE(obs.obs_datetime) = (SELECT MAX(DATE(o.obs_datetime)) FROM obs o
502
+ INNER JOIN encounter e ON e.encounter_id = o.encounter_id
503
+ AND e.program_id = 1 AND e.voided = 0 #{site_manager(operator: 'AND', column: 'e.site_id', location: @location)}
504
+ WHERE o.concept_id IN(#{pregnant_concepts.join(',')})
505
+ AND o.voided = 0 AND o.person_id = obs.person_id #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
506
+ AND o.obs_datetime <= '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}')
507
+ GROUP BY #{@adapter == 'mysql2' ? 'obs.person_id' : 'obs.person_id, obs.value_coded'}
508
+ HAVING #{@adapter == 'mysql2' ? 'value_coded' : 'obs.value_coded'} = 1065
509
+ ORDER BY MAX(obs.obs_datetime) DESC
510
+ SQL
511
+
512
+ female_maternal_status = results.blank? ? 'FNP' : 'FP'
513
+
514
+ if female_maternal_status == 'FNP'
515
+
516
+ breastfeeding_concepts = []
517
+ breastfeeding_concepts << concept_name('Breast feeding?').concept_id
518
+ breastfeeding_concepts << concept_name('Breast feeding').concept_id
519
+ breastfeeding_concepts << concept_name('Breastfeeding').concept_id
520
+
521
+ results2 = ActiveRecord::Base.connection.select_all <<~SQL
522
+ SELECT person_id, obs.value_coded value_coded
523
+ FROM obs obs
524
+ INNER JOIN encounter enc ON enc.encounter_id = obs.encounter_id AND enc.encounter_type IN(#{encounter_types.join(',')}) AND enc.voided = 0 AND enc.program_id = 1 #{site_manager(operator: 'AND', column: 'enc.site_id', location: @location)}
525
+ WHERE obs.person_id =#{patient_id}
526
+ AND obs.obs_datetime <= '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
527
+ AND obs.concept_id IN(#{breastfeeding_concepts.join(',')})
528
+ AND obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
529
+ AND DATE(obs.obs_datetime) = (SELECT MAX(DATE(o.obs_datetime)) FROM obs o
530
+ INNER JOIN encounter e ON e.encounter_id = o.encounter_id
531
+ AND e.program_id = 1 AND e.voided = 0 #{site_manager(operator: 'AND', column: 'e.site_id', location: @location)}
532
+ WHERE o.concept_id IN(#{breastfeeding_concepts.join(',')}) AND o.voided = 0
533
+ AND o.person_id = obs.person_id #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
534
+ AND o.obs_datetime <='#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}')
535
+ GROUP BY #{@adapter == 'mysql2' ? 'obs.person_id' : 'obs.person_id, obs.value_coded'}
536
+ HAVING #{@adapter == 'mysql2' ? 'value_coded' : 'obs.value_coded'} = 1065
537
+ ORDER BY MAX(obs.obs_datetime) DESC
538
+ SQL
539
+
540
+ female_maternal_status = results2.blank? ? 'FNP' : 'FBf'
541
+ end
542
+
543
+ results = ActiveRecord::Base.connection.select_all <<~SQL
544
+ SELECT person_id, obs.value_coded value_coded
545
+ FROM obs obs
546
+ INNER JOIN encounter enc ON enc.encounter_id = obs.encounter_id AND enc.encounter_type IN(#{encounter_types.join(',')}) AND enc.voided = 0 AND enc.program_id = 1 #{site_manager(operator: 'AND', column: 'enc.site_id', location: @location)}
547
+ WHERE obs.person_id = #{patient_id}
548
+ AND obs.obs_datetime <= '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
549
+ AND obs.concept_id IN(#{pregnant_concepts.join(',')})
550
+ AND obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
551
+ AND DATE(obs.obs_datetime) = (SELECT DATE(es.earliest_start_date) FROM temp_earliest_start_date es
552
+ WHERE es.patient_id = obs.person_id #{site_manager(operator: 'AND', column: 'es.site_id', location: @location)})
553
+ GROUP BY #{@adapter == 'mysql2' ? 'obs.person_id' : 'obs.person_id, obs.value_coded'}
554
+ HAVING #{@adapter == 'mysql2' ? 'value_coded' : 'obs.value_coded'} = 1065
555
+ ORDER BY MAX(obs.obs_datetime) DESC
556
+ SQL
557
+
558
+ initial_female_maternal_status = results.blank? ? 'FNP' : 'FP'
559
+
560
+ if initial_female_maternal_status == 'FNP'
561
+
562
+ breastfeeding_concepts = []
563
+ breastfeeding_concepts << concept_name('Breast feeding?').concept_id
564
+ breastfeeding_concepts << concept_name('Breast feeding').concept_id
565
+ breastfeeding_concepts << concept_name('Breastfeeding').concept_id
566
+
567
+ results2 = ActiveRecord::Base.connection.select_all <<~SQL
568
+ SELECT person_id, obs.value_coded value_coded
569
+ FROM obs obs
570
+ INNER JOIN encounter enc ON enc.encounter_id = obs.encounter_id AND enc.encounter_type IN(#{encounter_types.join(',')}) AND enc.voided = 0 AND enc.program_id = 1 #{site_manager(operator: 'AND', column: 'enc.site_id', location: @location)}
571
+ WHERE obs.person_id =#{patient_id}
572
+ AND obs.obs_datetime <= '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
573
+ AND obs.concept_id IN(#{breastfeeding_concepts.join(',')})
574
+ AND obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
575
+ AND DATE(obs.obs_datetime) = (SELECT DATE(es.earliest_start_date) FROM temp_earliest_start_date es
576
+ WHERE es.patient_id = obs.person_id #{site_manager(operator: 'AND', column: 'es.site_id', location: @location)})
577
+ GROUP BY #{@adapter == 'mysql2' ? 'obs.person_id' : 'obs.person_id, obs.value_coded'}
578
+ HAVING #{@adapter == 'mysql2' ? 'value_coded' : 'obs.value_coded'} = 1065
579
+ ORDER BY MAX(obs.obs_datetime) DESC
580
+ SQL
581
+
582
+ initial_female_maternal_status = results2.blank? ? 'FNP' : 'FBf'
583
+ end
584
+
585
+ ActiveRecord::Base.connection.execute <<~SQL
586
+ UPDATE temp_disaggregated SET maternal_status = '#{female_maternal_status}',
587
+ initial_maternal_status = '#{initial_female_maternal_status}',
588
+ age_group = '#{age_group}' WHERE patient_id = #{patient_id} AND site_id = #{@location};
589
+ SQL
590
+ end
591
+
592
+ def big_insert(data, age_group)
593
+ insert_array = []
594
+ (data || []).each do |r|
595
+ insert_array << "(#{r['patient_id']}, '#{age_group}', #{r['site_id']})"
596
+ end
597
+
598
+ return if insert_array.blank?
599
+
600
+ ActiveRecord::Base.connection.execute <<~SQL
601
+ INSERT INTO temp_disaggregated (patient_id, age_group, site_id)
602
+ VALUES #{insert_array.join(',')};
603
+ SQL
604
+ end
605
+ end
606
+ # rubocop:enable Metrics/ClassLength
607
+ end
608
+ end