malawi_hiv_program_reports 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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