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,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