malawi_hiv_program_reports 1.1.16 → 1.1.17

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 394a402d0b5e0c9b210bbf5feff0959854bb2b8d1322ecd97b07107201dcd694
4
- data.tar.gz: 51615fcd686c06088e928b6b10e1bba26a754825e9c3d891e0e7c43c00647098
3
+ metadata.gz: c818eb00df67da231594873c08c20bb5df18a2c53433788a96e27a7ca1211bf9
4
+ data.tar.gz: fd99e8c2ca139a33ddd76a9eefbaef20dfcf6eee7c1b6290919e2240c39b9c63
5
5
  SHA512:
6
- metadata.gz: aa30a38678f9f6dbeded4d8b3fe6ae920a46c6bea63937906f330cdf50b2cbc1ca6ae95edcf004caa2b4ca5501c6dbf627210f2f496dd49e1ba3964815e3a28f
7
- data.tar.gz: cccee12cb7e4e5d9e5e685230b63309f93ad6db7199de3ae244d461a95e0f825f71a0d82f9b01a535636ad2c45fffc617dfcc68460d1f9508853c059ee71be9e
6
+ metadata.gz: f61982a9e3ce30521079abacdd89144d6c03d10fde293d5fbefcb61fac57a143e5680f5216eee22e8423f37d8b88014205f602003af0644a7a95078fed7d052c
7
+ data.tar.gz: ea196faf6b0a471e995e4885058acbc0a141c7fcbe948e502801648ed796fbea5e72bf1d0f28b556ea0ccd1404cbc78046928ebb5330acdcfd7068f54f16c924
@@ -29,7 +29,7 @@ module MalawiHivProgramReports
29
29
  rebuild_report if rebuild
30
30
  process_initialization
31
31
  process_data
32
- flatten_and_sort_data
32
+ flatten_the_report
33
33
  rescue StandardError => e
34
34
  Rails.logger.info("Error processing location #{location}: #{e.message}")
35
35
  Rails.logger.info(e.backtrace.join("\n"))
@@ -38,12 +38,12 @@ module MalawiHivProgramReports
38
38
 
39
39
  private
40
40
 
41
- GENDER = %w[M F].freeze
42
- AGGREGATE_GENDER_ROWS = %w[M FP FNP FBf].freeze
41
+ GENDER = %w[Male Female].freeze
42
+ AGGREGATE_GENDER_ROWS = %w[Male FP FNP FBf].freeze
43
43
 
44
44
  def process_initialization
45
45
  init_report
46
- init_aggregate_rows if aggregation
46
+ init_aggregate_rows
47
47
  end
48
48
 
49
49
  def init_report
@@ -85,6 +85,7 @@ module MalawiHivProgramReports
85
85
  regimen = data['regimen'] || 'unknown'
86
86
  regimen = 'unknown' unless COHORT_REGIMENS.include?(regimen)
87
87
  patient_id = data['patient_id']
88
+ maternal_status = data['maternal_status']
88
89
  # we need to handle regimes that only have one P to become PP. Otherwise if it is already PP or PA we leave
89
90
  # it as is. Regimens are in this format NUMBERLETTERS
90
91
  regimen = regimen.gsub(/(\d+[A-Za-z]*P)\z/, '\1P') if regimen.match?(/\A\d+[A-Za-z]*[^P]P\z/)
@@ -92,63 +93,65 @@ module MalawiHivProgramReports
92
93
  report[age_group.to_s][gender.to_s][regimen.to_s] << patient_id
93
94
  report[age_group.to_s][gender.to_s]['tx_curr'] << patient_id
94
95
  report[age_group.to_s][gender.to_s]['total'] << patient_id
95
- process_aggregate_rows(gender:, regimen:, patient_id:) if aggregation
96
+ process_aggregate_rows(gender:, regimen:, patient_id:, maternal_status:)
96
97
  end
97
98
  end
98
99
 
99
- def process_maternal_data
100
- result = MalawiHivProgramReports::Pepfar::ViralLoadCoverage2.new(start_date:, end_date:, location:)
101
- .vl_maternal_status(maternal.keys)
102
- # result comes in this form: { FP: [], FBf: [] }
103
- # we need to loop through the keys
104
- result.each_key do |key|
105
- result[key].each do |patient_id|
106
- report['All'][key.to_s]['tx_curr'] << patient_id
107
- report['All'][key.to_s][maternal[patient_id].to_s] << patient_id
108
- report['All'][key.to_s]['total'] << patient_id
109
- end
110
- end
111
- end
112
- # rubocop:enable Metrics/AbcSize
113
-
114
- def process_aggregate_rows(gender:, regimen:, patient_id:)
100
+ def process_aggregate_rows(gender:, regimen:, patient_id:, maternal_status: nil)
115
101
  if gender == 'M'
116
102
  report['All']['M']['tx_curr'] << patient_id
117
103
  report['All']['M'][regimen.to_s] << patient_id
118
104
  report['All']['M']['total'] << patient_id
119
- else
120
- maternal[patient_id] = regimen
105
+ return
121
106
  end
122
- end
123
107
 
124
- # we need to flatten the data
125
- def flatten_data
126
- flat_data = []
127
- report.each do |age_group, gender_data|
128
- gender_data.each do |gender, regimen_data|
129
- flat_data << {
130
- 'age_group' => age_group,
131
- 'gender' => gender,
132
- **regimen_data
133
- }
134
- end
108
+ case maternal_status
109
+ when 'FP'
110
+ report['All']['FP']['tx_curr'] << patient_id
111
+ report['All']['FP'][regimen.to_s] << patient_id
112
+ report['All']['FP']['total'] << patient_id
113
+ when 'FBf'
114
+ report['All']['FBf']['tx_curr'] << patient_id
115
+ report['All']['FBf'][regimen.to_s] << patient_id
116
+ report['All']['FBf']['total'] << patient_id
117
+ else
118
+ report['All']['FNP']['tx_curr'] << patient_id
119
+ report['All']['FNP'][regimen.to_s] << patient_id
120
+ report['All']['FNP']['total'] << patient_id
135
121
  end
136
- flat_data
137
122
  end
123
+ # rubocop:enable Metrics/AbcSize
138
124
 
139
- def flatten_and_sort_data
140
- flat_data = flatten_data
125
+ def flatten_the_report
126
+ result = []
127
+ @report.each do |age_group, age_group_report|
128
+ next if age_group == 'Unknown'
141
129
 
142
- age_group_order = ['Unknown', '<1 year', '1-4 years', '5-9 years', '10-14 years', '15-19 years',
143
- '20-24 years', '25-29 years', '30-34 years', '35-39 years', '40-44 years', '45-49 years',
144
- '50-54 years', '55-59 years', '60-64 years', '65-69 years', '70-74 years', '75-79 years',
145
- '80-84 years', '85-89 years', '90 plus years', 'All']
146
- gender_order = %w[F M FP FNP FBF]
130
+ age_group_report.each_key do |gender|
131
+ next if gender == 'Unknown'
147
132
 
148
- flat_data.sort_by do |row|
149
- [age_group_order.index(row['age_group']) || Float::INFINITY,
150
- gender_order.index(row['gender']) || Float::INFINITY]
133
+ result << process_age_group_report(age_group, gender, age_group_report[gender])
134
+ end
151
135
  end
136
+
137
+ new_group = pepfar_age_groups.map { |age_group| age_group }
138
+ gender_scores = { 'Female' => 0, 'Male' => 1, 'FNP' => 3, 'FP' => 2, 'FBf' => 4 }
139
+ result_scores = result.sort_by do |item|
140
+ gender_score = gender_scores[item[:gender]] || 999
141
+ age_group_score = new_group.index(item[:age_group]) || 999
142
+ [gender_score, age_group_score]
143
+ end
144
+ # remove all unknown age groups
145
+ result_scores.reject { |item| item[:age_group].match?(/unknown/i) }
146
+ end
147
+
148
+ def process_age_group_report(age_group, gender, age_group_report)
149
+ {
150
+ age_group:,
151
+ gender:,
152
+ # spread the age group report
153
+ **age_group_report
154
+ }
152
155
  end
153
156
 
154
157
  # rubocop:disable Metrics/MethodLength
@@ -158,16 +161,23 @@ module MalawiHivProgramReports
158
161
  prescriptions.patient_id,
159
162
  COALESCE(regimens.name, 'unknown') AS regimen,
160
163
  prescriptions.age_group,
161
- prescriptions.gender
164
+ CASE prescriptions.gender
165
+ WHEN 'M' THEN 'Male'
166
+ WHEN 'F' THEN 'Female'
167
+ ELSE 'Unknown'
168
+ END gender,
169
+ prescriptions.maternal_status
162
170
  FROM (
163
171
  SELECT
164
172
  tcm.patient_id,
165
173
  GROUP_CONCAT(DISTINCT(tcm.drug_id) ORDER BY tcm.drug_id ASC) AS drugs,
166
174
  disaggregated_age_group(date(earliest_start_date.birthdate), date('#{end_date}')) AS age_group,
175
+ c.maternal_status,
167
176
  earliest_start_date.gender
168
177
  FROM cdr_temp_current_medication #{current_partition} tcm
169
178
  INNER JOIN cdr_temp_patient_outcomes #{current_partition} AS outcomes ON outcomes.patient_id = tcm.patient_id AND outcomes.cum_outcome = 'On antiretrovirals'
170
179
  INNER JOIN cdr_temp_cohort_members #{current_partition} AS earliest_start_date ON earliest_start_date.patient_id = tcm.patient_id AND earliest_start_date.gender IN ('M','F')
180
+ LEFT JOIN cdr_temp_maternal_status #{current_partition} c ON c.patient_id = tcm.patient_id
171
181
  GROUP BY tcm.patient_id
172
182
  ) AS prescriptions
173
183
  LEFT JOIN (
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: true
2
+ #
3
+ require 'redis'
2
4
 
3
5
  module MalawiHivProgramReports
4
6
  module Moh
@@ -72,7 +74,7 @@ module MalawiHivProgramReports
72
74
  ActiveRecord::Base.connection_pool.with_connection do
73
75
  process_data loc
74
76
  current_iteration = iteration.increment
75
- update_progress(current_iteration, locations.size)
77
+ # update_progress(current_iteration, locations.size)
76
78
  rescue StandardError => e
77
79
  Rails.logger.info("Error processing location #{loc}: #{e.message}")
78
80
  Rails.logger.info(e.backtrace.join("\n"))
@@ -161,13 +163,22 @@ module MalawiHivProgramReports
161
163
  outcome = MalawiHivProgramReports::Moh::CumulativeOutcome.new(end_date:, location:, definition:, rebuild:,
162
164
  start_date:)
163
165
  rebuild ? outcome.find_report : outcome.update_outcomes_by_definition
166
+ other_dependables(location)
164
167
  end_time = Time.now
165
168
  time_taken = ((end_time - start_time) / 60).round(2)
166
169
  save_completed_site(location:, time_taken:)
167
170
  end
168
171
 
172
+ def other_dependables(loc)
173
+ Pepfar::TxTb.new(start_date:, end_date:, location: loc, occupation: nil).process_tb_data
174
+ Pepfar::MaternalStatus.new(start_date:, end_date:, location: loc).process_data
175
+ end
176
+
169
177
  # rubocop:disable Metrics/CyclomaticComplexity
170
178
  def prepare_tables
179
+ create_cdr_temp_maternal_status unless check_if_table_exists('cdr_temp_maternal_status')
180
+ create_cdr_tb_confirmed_and_on_treatment unless check_if_table_exists('cdr_tb_confirmed_and_on_treatment')
181
+ create_tb_screened unless check_if_table_exists('cdr_tb_screened')
171
182
  create_cdr_other_patient_types unless check_if_table_exists('cdr_other_patient_types')
172
183
  create_temp_potential_cohort_members_table unless check_if_table_exists('cdr_temp_potential_cohort_members')
173
184
  create_min_drug_orders_table unless check_if_table_exists('cdr_temp_min_drug_orders')
@@ -193,6 +204,18 @@ module MalawiHivProgramReports
193
204
  SQL
194
205
  end
195
206
 
207
+ def create_cdr_temp_maternal_status
208
+ ActiveRecord::Base.connection.execute <<~SQL
209
+ CREATE TABLE IF NOT EXISTS cdr_temp_maternal_status (
210
+ patient_id INT(11) NOT NULL,
211
+ site_id INT(11) NOT NULL,
212
+ maternal_status VARCHAR(5) DEFAULT NULL,
213
+ PRIMARY KEY (patient_id, site_id)
214
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
215
+ PARTITION BY LIST(site_id) (#{partition_by_site})
216
+ SQL
217
+ end
218
+
196
219
  def create_cdr_temp_cohort_status
197
220
  ActiveRecord::Base.connection.execute <<~SQL
198
221
  CREATE TABLE IF NOT EXISTS cdr_temp_cohort_status (
@@ -303,6 +326,40 @@ module MalawiHivProgramReports
303
326
  SQL
304
327
  end
305
328
 
329
+ def create_cdr_tb_confirmed_and_on_treatment
330
+ ActiveRecord::Base.connection.execute <<~SQL
331
+ CREATE TABLE IF NOT EXISTS cdr_tb_confirmed_and_on_treatment (
332
+ patient_id INT(11) NOT NULL,
333
+ site_id INT(11) NOT NULL,
334
+ gender VARCHAR(50) DEFAULT NULL,
335
+ age_group VARCHAR(50) DEFAULT NULL,
336
+ tb_confirmed_date DATE DEFAULT NULL,
337
+ has_tb_confirmed_date BOOLEAN DEFAULT NULL,
338
+ enrollment_date DATE DEFAULT NULL,
339
+ prev_reading DATE DEFAULT NULL,
340
+ PRIMARY KEY (patient_id, site_id)
341
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
342
+ PARTITION BY LIST(site_id) (#{partition_by_site})
343
+ SQL
344
+ end
345
+
346
+ def create_tb_screened
347
+ ActiveRecord::Base.connection.execute <<~SQL
348
+ CREATE TABLE IF NOT EXISTS cdr_tb_screened (
349
+ patient_id INT(11) NOT NULL,
350
+ site_id INT(11) NOT NULL,
351
+ gender VARCHAR(255) NULL,
352
+ screened_date DATE NULL,
353
+ enrollment_date DATE NULL,
354
+ age_group VARCHAR(255) NULL,
355
+ tb_status VARCHAR(255) NULL,
356
+ screening_methods VARCHAR(255) NULL,
357
+ PRIMARY KEY (patient_id, site_id)
358
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8
359
+ PARTITION BY LIST(site_id) (#{partition_by_site})
360
+ SQL
361
+ end
362
+
306
363
  def create_cdr_other_patient_types
307
364
  ActiveRecord::Base.connection.execute <<~SQL
308
365
  CREATE TABLE IF NOT EXISTS cdr_other_patient_types (
@@ -485,6 +542,8 @@ module MalawiHivProgramReports
485
542
  def clear_tables
486
543
  # if locations is empty then we truncating otherwise we clear the locations
487
544
  if locations.empty?
545
+ ActiveRecord::Base.connection.execute('TRUNCATE TABLE cdr_tb_confirmed_and_on_treatment')
546
+ ActiveRecord::Base.connection.execute('TRUNCATE TABLE cdr_tb_screened')
488
547
  ActiveRecord::Base.connection.execute('TRUNCATE TABLE cdr_other_patient_types')
489
548
  ActiveRecord::Base.connection.execute('TRUNCATE TABLE cdr_temp_potential_cohort_members')
490
549
  ActiveRecord::Base.connection.execute('TRUNCATE TABLE cdr_temp_min_drug_orders')
@@ -493,6 +552,8 @@ module MalawiHivProgramReports
493
552
  ActiveRecord::Base.connection.execute('TRUNCATE TABLE cdr_temp_external_clients')
494
553
  ActiveRecord::Base.connection.execute('TRUNCATE TABLE cdr_reason_for_starting_art')
495
554
  else
555
+ ActiveRecord::Base.connection.execute("DELETE FROM cdr_tb_confirmed_and_on_treatment WHERE site_id IN (#{locations.join(',')})")
556
+ ActiveRecord::Base.connection.execute("DELETE FROM cdr_tb_screened WHERE site_id IN (#{locations.join(',')})")
496
557
  ActiveRecord::Base.connection.execute("DELETE FROM cdr_other_patient_types WHERE site_id IN (#{locations.join(',')})")
497
558
  ActiveRecord::Base.connection.execute("DELETE FROM cdr_temp_potential_cohort_members WHERE site_id IN (#{locations.join(',')})")
498
559
  ActiveRecord::Base.connection.execute("DELETE FROM cdr_temp_min_drug_orders WHERE site_id IN (#{locations.join(',')})")
@@ -22,7 +22,109 @@ module MalawiHivProgramReports
22
22
  end
23
23
 
24
24
  def find_report
25
- MalawiHivProgramReports::Pepfar::ViralLoadCoverage2.new(tx_curr_definition: 'pepfar', start_date: @start_date, end_date: @end_date, location: @location).vl_maternal_status(@patient_ids)
25
+ vl_maternal_status
26
+ end
27
+
28
+ def process_data
29
+ clear_maternal_status
30
+ load_pregnant_women
31
+ load_breast_feeding
32
+ end
33
+
34
+ private
35
+
36
+ def vl_maternal_status(patient_list)
37
+ return { FP: [], FBf: [] } if patient_list.blank?
38
+
39
+ pregnant = pregnant_women(patient_list).map { |woman| woman['person_id'].to_i }
40
+ return { FP: pregnant, FBf: [] } if (patient_list - pregnant).blank?
41
+
42
+ feeding = breast_feeding(patient_list - pregnant).map { |woman| woman['person_id'].to_i }
43
+
44
+ {
45
+ FP: pregnant,
46
+ FBf: feeding
47
+ }
48
+ end
49
+
50
+ def pregnant_women(patient_list)
51
+ ActiveRecord::Base.connection.execute <<~SQL
52
+ SELECT person_id
53
+ FROM cdr_temp_maternal_status #{current_partition}
54
+ WHERE maternal_status = 'FP' AND patient_od IN (#{patient_list.join(',')})
55
+ SQL
56
+ end
57
+
58
+ def breast_feeding(patient_list)
59
+ ActiveRecord::Base.connection.execute <<~SQL
60
+ SELECT person_id
61
+ FROM cdr_temp_maternal_status #{current_partition}
62
+ WHERE maternal_status = 'FBf' AND patient_id IN (#{patient_list.join(',')})
63
+ SQL
64
+ end
65
+
66
+ def load_pregnant_women
67
+ ActiveRecord::Base.connection.execute <<~SQL
68
+ INSERT INTO cdr_temp_maternal_status #{current_partition} (patient_id, site_id, maternal_status)
69
+ SELECT o.person_id, #{location}, 'FP' as maternal_status
70
+ FROM obs #{current_partition} o
71
+ INNER JOIN cdr_temp_cohort_members #{current_partition} c ON c.patient_id = o.person_id AND c.gender = 'F'
72
+ LEFT JOIN obs #{current_partition} a ON a.person_id = o.person_id AND a.obs_datetime > o.obs_datetime AND a.concept_id IN (#{pregnant_concepts.to_sql}) AND a.voided = 0
73
+ AND a.obs_datetime >= DATE(#{ActiveRecord::Base.connection.quote(start_date)}) AND a.obs_datetime < DATE(#{ActiveRecord::Base.connection.quote(end_date)}) + INTERVAL 1 DAY
74
+ WHERE a.obs_id is null
75
+ AND o.obs_datetime >= DATE(#{ActiveRecord::Base.connection.quote(start_date)})
76
+ AND o.obs_datetime < DATE(#{ActiveRecord::Base.connection.quote(end_date)}) + INTERVAL 1 DAY
77
+ AND o.voided = 0
78
+ AND o.concept_id in (#{pregnant_concepts.to_sql})
79
+ AND o.value_coded IN (#{yes_concepts.join(',')})
80
+ GROUP BY o.person_id
81
+ SQL
82
+ end
83
+
84
+ def load_breast_feeding
85
+ ActiveRecord::Base.connection.execute <<~SQL
86
+ INSERT INTO cdr_temp_maternal_status #{current_partition} (patient_id, site_id, maternal_status)
87
+ SELECT o.person_id, #{location}, 'FBf' as maternal_status
88
+ FROM obs #{current_partition} o
89
+ INNER JOIN cdr_temp_cohort_members #{current_partition} c ON c.patient_id = o.person_id AND c.gender = 'F'
90
+ LEFT JOIN obs #{current_partition} a ON a.person_id = o.person_id AND a.obs_datetime > o.obs_datetime AND a.concept_id IN (#{breast_feeding_concepts.to_sql}) AND a.voided = 0
91
+ AND a.obs_datetime >= DATE(#{ActiveRecord::Base.connection.quote(start_date)}) AND a.obs_datetime < DATE(#{ActiveRecord::Base.connection.quote(end_date)}) + INTERVAL 1 DAY
92
+ WHERE a.obs_id is null
93
+ AND o.obs_datetime >= DATE(#{ActiveRecord::Base.connection.quote(start_date)})
94
+ AND o.obs_datetime < DATE(#{ActiveRecord::Base.connection.quote(end_date)}) + INTERVAL 1 DAY
95
+ AND o.voided = 0
96
+ AND o.concept_id IN (#{breast_feeding_concepts.to_sql})
97
+ AND o.value_coded IN (#{yes_concepts.join(',')})
98
+ AND o.person_id NOT IN (SELECT c.patient_id FROM cdr_temp_maternal_status #{current_partition} c)
99
+ GROUP BY o.person_id
100
+ SQL
101
+ end
102
+
103
+ def clear_maternal_status
104
+ ActiveRecord::Base.connection.execute <<~SQL
105
+ DELETE FROM cdr_temp_maternal_status where site_id = #{location}
106
+ SQL
107
+ end
108
+
109
+ def yes_concepts
110
+ @yes_concepts ||= ::ConceptName.where(name: 'Yes').select(:concept_id).map do |record|
111
+ record['concept_id'].to_i
112
+ end
113
+ end
114
+
115
+ def pregnant_concepts
116
+ @pregnant_concepts ||= ::ConceptName.where(name: ['Is patient pregnant?', 'patient pregnant'])
117
+ .select(:concept_id)
118
+ end
119
+
120
+ def breast_feeding_concepts
121
+ @breast_feeding_concepts ||= ::ConceptName.where(name: ['Breast feeding?', 'Breast feeding', 'Breastfeeding'])
122
+ .select(:concept_id)
123
+ end
124
+
125
+ def encounter_types
126
+ @encounter_types ||= ::EncounterType.where(name: ['HIV CLINIC CONSULTATION', 'HIV STAGING'])
127
+ .select(:encounter_type_id)
26
128
  end
27
129
  end
28
130
  end