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.
- checksums.yaml +4 -4
- data/app/services/malawi_hiv_program_reports/README.md +16 -0
- data/app/services/malawi_hiv_program_reports/adapters/moh/custom.rb +199 -0
- data/app/services/malawi_hiv_program_reports/archiving_candidates.rb +130 -0
- data/app/services/malawi_hiv_program_reports/arv_refill_periods.rb +311 -0
- data/app/services/malawi_hiv_program_reports/clinic/README.md +5 -0
- data/app/services/malawi_hiv_program_reports/clinic/appointments_report.rb +317 -0
- data/app/services/malawi_hiv_program_reports/clinic/discrepancy_report.rb +42 -0
- data/app/services/malawi_hiv_program_reports/clinic/docs/hypertension_report.md +31 -0
- data/app/services/malawi_hiv_program_reports/clinic/drug_dispensations.rb +48 -0
- data/app/services/malawi_hiv_program_reports/clinic/external_consultation_clients.rb +69 -0
- data/app/services/malawi_hiv_program_reports/clinic/hypertension_report.rb +223 -0
- data/app/services/malawi_hiv_program_reports/clinic/ipt_coverage.rb +112 -0
- data/app/services/malawi_hiv_program_reports/clinic/ipt_report.rb +69 -0
- data/app/services/malawi_hiv_program_reports/clinic/lims_results.rb +55 -0
- data/app/services/malawi_hiv_program_reports/clinic/outcome_list.rb +127 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_alive_and_on_treatment.rb +57 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_due_for_viral_load.rb +39 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_on_antiretrovirals.rb +44 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_on_dtg.rb +36 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_on_treatment.rb +42 -0
- data/app/services/malawi_hiv_program_reports/clinic/patients_with_outdated_demographics.rb +173 -0
- data/app/services/malawi_hiv_program_reports/clinic/pregnant_patients.rb +91 -0
- data/app/services/malawi_hiv_program_reports/clinic/regimen_dispensation_data.rb +282 -0
- data/app/services/malawi_hiv_program_reports/clinic/regimen_switch.rb +456 -0
- data/app/services/malawi_hiv_program_reports/clinic/regimens_and_formulations.rb +182 -0
- data/app/services/malawi_hiv_program_reports/clinic/regimens_by_weight_and_gender.rb +108 -0
- data/app/services/malawi_hiv_program_reports/clinic/retention.rb +246 -0
- data/app/services/malawi_hiv_program_reports/clinic/stock_card_report.rb +65 -0
- data/app/services/malawi_hiv_program_reports/clinic/tpt_outcome.rb +494 -0
- data/app/services/malawi_hiv_program_reports/clinic/tx_rtt.rb +169 -0
- data/app/services/malawi_hiv_program_reports/clinic/viral_load.rb +292 -0
- data/app/services/malawi_hiv_program_reports/clinic/viral_load_disaggregated.rb +97 -0
- data/app/services/malawi_hiv_program_reports/clinic/viral_load_results.rb +175 -0
- data/app/services/malawi_hiv_program_reports/clinic/visits_report.rb +113 -0
- data/app/services/malawi_hiv_program_reports/clinic/vl_collection.rb +48 -0
- data/app/services/malawi_hiv_program_reports/cohort/outcomes.rb +338 -0
- data/app/services/malawi_hiv_program_reports/cohort/regimens.rb +69 -0
- data/app/services/malawi_hiv_program_reports/cohort/side_effects.rb +141 -0
- data/app/services/malawi_hiv_program_reports/cohort/tpt.rb +172 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort.rb +278 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_builder.rb +2340 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_disaggregated.rb +608 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_disaggregated_additions.rb +208 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_disaggregated_builder.rb +526 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_struct.rb +219 -0
- data/app/services/malawi_hiv_program_reports/moh/cohort_survival_analysis.rb +203 -0
- data/app/services/malawi_hiv_program_reports/moh/moh_tpt.rb +223 -0
- data/app/services/malawi_hiv_program_reports/moh/tpt_newly_initiated.rb +235 -0
- data/app/services/malawi_hiv_program_reports/pepfar/defaulter_list.rb +25 -0
- data/app/services/malawi_hiv_program_reports/pepfar/maternal_status.rb +29 -0
- data/app/services/malawi_hiv_program_reports/pepfar/patient_start_vl.rb +45 -0
- data/app/services/malawi_hiv_program_reports/pepfar/regimen_switch.rb +479 -0
- data/app/services/malawi_hiv_program_reports/pepfar/sc_arvdisp.rb +174 -0
- data/app/services/malawi_hiv_program_reports/pepfar/sc_curr.rb +98 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tb_prev.rb +163 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tb_prev2.rb +222 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tb_prev3.rb +421 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tpt_status.rb +181 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tx_ml.rb +181 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tx_new.rb +202 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tx_rtt.rb +288 -0
- data/app/services/malawi_hiv_program_reports/pepfar/tx_tb.rb +283 -0
- data/app/services/malawi_hiv_program_reports/pepfar/utils.rb +141 -0
- data/app/services/malawi_hiv_program_reports/pepfar/viral_load_coverage.rb +414 -0
- data/app/services/malawi_hiv_program_reports/pepfar/viral_load_coverage2.rb +433 -0
- data/app/services/malawi_hiv_program_reports/report_map.rb +56 -0
- data/app/services/malawi_hiv_program_reports/utils/README.md +8 -0
- data/app/services/malawi_hiv_program_reports/utils/common_sql_query_utils.rb +60 -0
- data/app/services/malawi_hiv_program_reports/utils/concurrency_utils.rb +53 -0
- data/app/services/malawi_hiv_program_reports/utils/docs/common_sql_query_utils.md +53 -0
- data/app/services/malawi_hiv_program_reports/utils/model_utils.rb +66 -0
- data/app/services/malawi_hiv_program_reports/utils/parameter_utils.rb +32 -0
- data/app/services/malawi_hiv_program_reports/utils/time_utils.rb +52 -0
- data/lib/malawi_hiv_program_reports/version.rb +1 -1
- metadata +74 -1
|
@@ -0,0 +1,2340 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../cohort/tpt'
|
|
4
|
+
|
|
5
|
+
module MalawiHivProgramReports
|
|
6
|
+
module Moh
|
|
7
|
+
# This is the Cohort Builder class
|
|
8
|
+
# rubocop:disable Metrics/ClassLength
|
|
9
|
+
class CohortBuilder
|
|
10
|
+
QUARTER_LENGTH = 3.months
|
|
11
|
+
|
|
12
|
+
include MalawiHivProgramReports::Utils::ModelUtils
|
|
13
|
+
include MalawiHivProgramReports::Utils::CommonSqlQueryUtils
|
|
14
|
+
include MalawiHivProgramReports::Adapters::Moh::Custom
|
|
15
|
+
|
|
16
|
+
def initialize(outcomes_definition: 'moh', **kwargs)
|
|
17
|
+
unless %w[moh pepfar].include?(outcomes_definition.downcase)
|
|
18
|
+
raise ::ArgumentError, "Invalid outcomes_definition `#{outcomes_definition}` expected moh or pepfar"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@location = kwargs[:location]
|
|
22
|
+
@adapter = ActiveRecord::Base.connection.adapter_name.downcase
|
|
23
|
+
@outcomes_definition = outcomes_definition
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def init_temporary_tables(_start_date, end_date, occupation)
|
|
27
|
+
create_temp_cohort_members_table
|
|
28
|
+
create_tmp_patient_table
|
|
29
|
+
drop_temp_register_start_date_table
|
|
30
|
+
drop_temp_other_patient_types
|
|
31
|
+
drop_temp_order_details
|
|
32
|
+
create_temp_other_patient_types(end_date)
|
|
33
|
+
create_temp_register_start_date_table(end_date)
|
|
34
|
+
create_temp_order_details(end_date)
|
|
35
|
+
load_data_into_temp_earliest_start_date(end_date.to_date, occupation)
|
|
36
|
+
update_cum_outcome(end_date)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def build(cohort_struct, start_date, end_date, occupation)
|
|
40
|
+
# load_tmp_patient_table(cohort_struct)
|
|
41
|
+
create_temp_cohort_members_table
|
|
42
|
+
create_tmp_patient_table
|
|
43
|
+
drop_temp_register_start_date_table
|
|
44
|
+
drop_temp_other_patient_types
|
|
45
|
+
drop_temp_order_details
|
|
46
|
+
create_temp_other_patient_types(end_date)
|
|
47
|
+
create_temp_register_start_date_table(end_date)
|
|
48
|
+
create_temp_order_details(end_date)
|
|
49
|
+
load_data_into_temp_earliest_start_date(end_date.to_date, occupation)
|
|
50
|
+
|
|
51
|
+
# create_tmp_patient_table_2(end_date)
|
|
52
|
+
|
|
53
|
+
time_started = Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
|
54
|
+
|
|
55
|
+
# create_temp_earliest_start_date_table(end_date)
|
|
56
|
+
quarter_start_date = start_date.to_date
|
|
57
|
+
|
|
58
|
+
# Get earliest date enrolled
|
|
59
|
+
cum_start_date = get_cum_start_date
|
|
60
|
+
|
|
61
|
+
cum_start_date = start_date if cum_start_date.blank?
|
|
62
|
+
|
|
63
|
+
# Total registeres
|
|
64
|
+
cohort_struct.total_registered = total_registered(start_date, end_date)
|
|
65
|
+
cohort_struct.cum_total_registered = total_registered(cum_start_date, end_date)
|
|
66
|
+
cohort_struct.quarterly_total_registered = total_registered(quarter_start_date, end_date)
|
|
67
|
+
|
|
68
|
+
# Patients initiated on ART first time
|
|
69
|
+
cohort_struct.initiated_on_art_first_time = initiated_on_art_first_time(start_date, end_date)
|
|
70
|
+
cohort_struct.cum_initiated_on_art_first_time = initiated_on_art_first_time(cum_start_date, end_date)
|
|
71
|
+
cohort_struct.quarterly_initiated_on_art_first_time = initiated_on_art_first_time(quarter_start_date, end_date)
|
|
72
|
+
|
|
73
|
+
cohort_struct.males_initiated_on_art_first_time = males_initiated_on_art_first_time(start_date, end_date,
|
|
74
|
+
cohort_struct.initiated_on_art_first_time)
|
|
75
|
+
cohort_struct.cum_males_initiated_on_art_first_time = males_initiated_on_art_first_time(cum_start_date,
|
|
76
|
+
end_date, cohort_struct.cum_initiated_on_art_first_time)
|
|
77
|
+
|
|
78
|
+
# Patients re-initiated on ART
|
|
79
|
+
cohort_struct.re_initiated_on_art = re_initiated_on_art(start_date, end_date)
|
|
80
|
+
cohort_struct.cum_re_initiated_on_art = re_initiated_on_art(cum_start_date, end_date)
|
|
81
|
+
cohort_struct.quarterly_re_initiated_on_art = re_initiated_on_art(quarter_start_date, end_date)
|
|
82
|
+
|
|
83
|
+
# Patients transferred in on ART
|
|
84
|
+
cohort_struct.transfer_in = transfer_in(start_date, end_date, cohort_struct.re_initiated_on_art)
|
|
85
|
+
cohort_struct.cum_transfer_in = transfer_in(cum_start_date, end_date, cohort_struct.cum_re_initiated_on_art)
|
|
86
|
+
cohort_struct.quarterly_transfer_in = transfer_in(quarter_start_date, end_date,
|
|
87
|
+
cohort_struct.quarterly_re_initiated_on_art)
|
|
88
|
+
|
|
89
|
+
# All males
|
|
90
|
+
cohort_struct.all_males = males(start_date, end_date)
|
|
91
|
+
cohort_struct.cum_all_males = males(cum_start_date, end_date)
|
|
92
|
+
cohort_struct.quarterly_all_males = males(quarter_start_date, end_date)
|
|
93
|
+
|
|
94
|
+
# Pregnant females (all ages)
|
|
95
|
+
cohort_struct.pregnant_females_all_ages = pregnant_females_all_ages(start_date, end_date)
|
|
96
|
+
cohort_struct.cum_pregnant_females_all_ages = pregnant_females_all_ages(cum_start_date, end_date)
|
|
97
|
+
cohort_struct.quarterly_pregnant_females_all_ages = pregnant_females_all_ages(quarter_start_date, end_date)
|
|
98
|
+
|
|
99
|
+
cohort_struct.initial_pregnant_females_all_ages = initial_females_all_ages(start_date, end_date,
|
|
100
|
+
cohort_struct.pregnant_females_all_ages)
|
|
101
|
+
cohort_struct.cum_initial_pregnant_females_all_ages = initial_females_all_ages(cum_start_date, end_date,
|
|
102
|
+
cohort_struct.cum_pregnant_females_all_ages)
|
|
103
|
+
|
|
104
|
+
# Non-pregnant females (all ages)
|
|
105
|
+
# Unique PatientProgram entries at the current location for those patients with at least one state ON ARVs
|
|
106
|
+
# and earliest start date of the 'ON ARVs' state within the quarter and having gender of
|
|
107
|
+
# related PERSON entry as F for female and no entries of 'IS PATIENT PREGNANT?' observation answered 'YES'
|
|
108
|
+
# in related HIV CLINIC CONSULTATION encounters not within 28 days from earliest registration date
|
|
109
|
+
cohort_struct.non_pregnant_females = non_pregnant_females(start_date, end_date,
|
|
110
|
+
cohort_struct.pregnant_females_all_ages)
|
|
111
|
+
cohort_struct.cum_non_pregnant_females = non_pregnant_females(cum_start_date, end_date,
|
|
112
|
+
cohort_struct.cum_pregnant_females_all_ages)
|
|
113
|
+
cohort_struct.quarterly_non_pregnant_females = non_pregnant_females(quarter_start_date, end_date,
|
|
114
|
+
cohort_struct.cum_pregnant_females_all_ages)
|
|
115
|
+
|
|
116
|
+
cohort_struct.initial_non_pregnant_females_all_ages = initial_females_all_ages(start_date, end_date, cohort_struct.non_pregnant_females.map do |a|
|
|
117
|
+
a['patient_id']
|
|
118
|
+
end)
|
|
119
|
+
cohort_struct.cum_initial_non_pregnant_females_all_ages = initial_females_all_ages(cum_start_date, end_date, cohort_struct.cum_non_pregnant_females.map do |a|
|
|
120
|
+
a['patient_id']
|
|
121
|
+
end)
|
|
122
|
+
|
|
123
|
+
# Children below 24 months at ART initiation
|
|
124
|
+
cohort_struct.children_below_24_months_at_art_initiation = children_below_24_months_at_art_initiation(
|
|
125
|
+
start_date, end_date
|
|
126
|
+
)
|
|
127
|
+
cohort_struct.cum_children_below_24_months_at_art_initiation = children_below_24_months_at_art_initiation(
|
|
128
|
+
cum_start_date, end_date
|
|
129
|
+
)
|
|
130
|
+
cohort_struct.quarterly_children_below_24_months_at_art_initiation = children_below_24_months_at_art_initiation(
|
|
131
|
+
quarter_start_date, end_date
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Children 24 months – 14 years at ART initiation
|
|
135
|
+
cohort_struct.children_24_months_14_years_at_art_initiation = children_24_months_14_years_at_art_initiation(
|
|
136
|
+
start_date, end_date
|
|
137
|
+
)
|
|
138
|
+
cohort_struct.cum_children_24_months_14_years_at_art_initiation = children_24_months_14_years_at_art_initiation(
|
|
139
|
+
cum_start_date, end_date
|
|
140
|
+
)
|
|
141
|
+
cohort_struct.quarterly_children_24_months_14_years_at_art_initiation = children_24_months_14_years_at_art_initiation(
|
|
142
|
+
quarter_start_date, end_date
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Adults at ART initiation
|
|
146
|
+
cohort_struct.adults_at_art_initiation = adults_at_art_initiation(start_date, end_date)
|
|
147
|
+
cohort_struct.cum_adults_at_art_initiation = adults_at_art_initiation(cum_start_date, end_date)
|
|
148
|
+
cohort_struct.quarterly_adults_at_art_initiation = adults_at_art_initiation(quarter_start_date, end_date)
|
|
149
|
+
|
|
150
|
+
# Unknown age
|
|
151
|
+
cohort_struct.unknown_age = unknown_age(start_date, end_date)
|
|
152
|
+
cohort_struct.cum_unknown_age = unknown_age(cum_start_date, end_date)
|
|
153
|
+
cohort_struct.quarterly_unknown_age = unknown_age(quarter_start_date, end_date)
|
|
154
|
+
|
|
155
|
+
# Unknown gender
|
|
156
|
+
cohort_struct.unknown_gender = unknown_gender(start_date, end_date)
|
|
157
|
+
cohort_struct.cum_unknown_gender = unknown_gender(cum_start_date, end_date)
|
|
158
|
+
|
|
159
|
+
# Unique PatientProgram entries at the current location for those
|
|
160
|
+
# patients with at least one state ON ARVs and earliest start date
|
|
161
|
+
# of the 'ON ARVs' state within the quarter and having a
|
|
162
|
+
# REASON FOR ELIGIBILITY observation with an answer as PRESUMED SEVERE HIV
|
|
163
|
+
cohort_struct.presumed_severe_hiv_disease_in_infants = presumed_severe_hiv_disease_in_infants(start_date,
|
|
164
|
+
end_date)
|
|
165
|
+
cohort_struct.cum_presumed_severe_hiv_disease_in_infants = presumed_severe_hiv_disease_in_infants(
|
|
166
|
+
cum_start_date, end_date
|
|
167
|
+
)
|
|
168
|
+
cohort_struct.quarterly_presumed_severe_hiv_disease_in_infants = presumed_severe_hiv_disease_in_infants(
|
|
169
|
+
quarter_start_date, end_date
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Confirmed HIV infection in infants (PCR)
|
|
173
|
+
|
|
174
|
+
# Unique PatientProgram entries at the current location for those patients with at least one state ON ARVs
|
|
175
|
+
# and earliest start date of the 'ON ARVs' state within the quarter and
|
|
176
|
+
# having a REASON FOR ELIGIBILITY observation with an answer as HIV PCR
|
|
177
|
+
cohort_struct.confirmed_hiv_infection_in_infants_pcr = confirmed_hiv_infection_in_infants_pcr(start_date,
|
|
178
|
+
end_date)
|
|
179
|
+
cohort_struct.cum_confirmed_hiv_infection_in_infants_pcr = confirmed_hiv_infection_in_infants_pcr(
|
|
180
|
+
cum_start_date, end_date
|
|
181
|
+
)
|
|
182
|
+
cohort_struct.quarterly_confirmed_hiv_infection_in_infants_pcr = confirmed_hiv_infection_in_infants_pcr(
|
|
183
|
+
quarter_start_date, end_date
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# WHO stage 1 or 2, CD4 below threshold
|
|
187
|
+
# Unique PatientProgram entries at the current location for those patients with at least one state ON ARVs
|
|
188
|
+
# and earliest start date of the 'ON ARVs' state within the quarter and having a REASON FOR ELIGIBILITY
|
|
189
|
+
# observation with an answer as CD4 COUNT LESS THAN OR EQUAL TO 350 or CD4 COUNT LESS THAN OR EQUAL TO 750
|
|
190
|
+
cohort_struct.who_stage_two = who_stage_two(start_date, end_date)
|
|
191
|
+
cohort_struct.cum_who_stage_two = who_stage_two(cum_start_date, end_date)
|
|
192
|
+
cohort_struct.quarterly_who_stage_two = who_stage_two(quarter_start_date, end_date)
|
|
193
|
+
|
|
194
|
+
# Breastfeeding mothers
|
|
195
|
+
|
|
196
|
+
# Unique PatientProgram entries at the current location for those patients with at least one state
|
|
197
|
+
# ON ARVs and earliest start date of the 'ON ARVs' state within the quarter
|
|
198
|
+
# and having a REASON FOR ELIGIBILITY observation with an answer as BREASTFEEDING
|
|
199
|
+
cohort_struct.breastfeeding_mothers = breastfeeding_mothers(start_date, end_date)
|
|
200
|
+
cohort_struct.cum_breastfeeding_mothers = breastfeeding_mothers(cum_start_date, end_date)
|
|
201
|
+
cohort_struct.quarterly_breastfeeding_mothers = breastfeeding_mothers(quarter_start_date, end_date)
|
|
202
|
+
|
|
203
|
+
# Pregnant women
|
|
204
|
+
|
|
205
|
+
# Unique PatientProgram entries at the current location for those patients with at least one state ON ARVs
|
|
206
|
+
# and earliest start date of the 'ON ARVs' state within the quarter
|
|
207
|
+
# and having a REASON FOR ELIGIBILITY observation with an answer as PATIENT PREGNANT
|
|
208
|
+
cohort_struct.pregnant_women = pregnant_women(start_date, end_date)
|
|
209
|
+
cohort_struct.cum_pregnant_women = pregnant_women(cum_start_date, end_date)
|
|
210
|
+
cohort_struct.quarterly_pregnant_women = pregnant_women(quarter_start_date, end_date)
|
|
211
|
+
|
|
212
|
+
# WHO STAGE 3
|
|
213
|
+
# Unique PatientProgram entries at the current location for those patients with at least
|
|
214
|
+
# one state ON ARVs and earliest start date of the 'ON ARVs' state within the quarter
|
|
215
|
+
# and having a REASON FOR ELIGIBILITY observation with an answer as WHO STAGE III
|
|
216
|
+
cohort_struct.who_stage_three = who_stage_three(start_date, end_date)
|
|
217
|
+
cohort_struct.cum_who_stage_three = who_stage_three(cum_start_date, end_date)
|
|
218
|
+
cohort_struct.quarterly_who_stage_three = who_stage_three(quarter_start_date, end_date)
|
|
219
|
+
|
|
220
|
+
# WHO STAGE 4
|
|
221
|
+
# Unique PatientProgram entries at the current location for those patients with at least
|
|
222
|
+
# one state ON ARVs and earliest start date of the 'ON ARVs' state within the quarter
|
|
223
|
+
# and having a REASON FOR ELIGIBILITY observation with an answer as WHO STAGE IV
|
|
224
|
+
cohort_struct.who_stage_four = who_stage_four(start_date, end_date)
|
|
225
|
+
cohort_struct.cum_who_stage_four = who_stage_four(cum_start_date, end_date)
|
|
226
|
+
cohort_struct.quarterly_who_stage_four = who_stage_four(quarter_start_date, end_date)
|
|
227
|
+
|
|
228
|
+
# Asymptomatic
|
|
229
|
+
# Unique PatientProgram entries at the current location for those patients with at least
|
|
230
|
+
# one state ON ARVs and earliest start date of the 'ON ARVs' state within the quarter
|
|
231
|
+
# and having a REASON FOR ELIGIBILITY observation with an answer as Lymphocytes
|
|
232
|
+
# or LYMPHOCYTE COUNT BELOW THRESHOLD WITH WHO STAGE 2
|
|
233
|
+
|
|
234
|
+
# For all those patients with WHO stage 1 and 2, only those that were enrolled
|
|
235
|
+
# after or on 2016-04-01 revised_guidelines_start_date = "2016-04-01"
|
|
236
|
+
cohort_struct.asymptomatic = asymptomatic(start_date, end_date)
|
|
237
|
+
cohort_struct.cum_asymptomatic = asymptomatic(cum_start_date, end_date)
|
|
238
|
+
cohort_struct.quarterly_asymptomatic = asymptomatic(quarter_start_date, end_date)
|
|
239
|
+
|
|
240
|
+
# Unknown / other reason outside guidelines
|
|
241
|
+
# Unique PatientProgram entries at the current location for those patients with at least one state ON ARVs
|
|
242
|
+
# and earliest start date of the 'ON ARVs' state within the quarter
|
|
243
|
+
# and having a REASON FOR ELIGIBILITY observation with an answer as UNKNOWN
|
|
244
|
+
cohort_struct.unknown_other_reason_outside_guidelines = unknown_other_reason_outside_guidelines(start_date,
|
|
245
|
+
end_date)
|
|
246
|
+
cohort_struct.cum_unknown_other_reason_outside_guidelines = unknown_other_reason_outside_guidelines(
|
|
247
|
+
cum_start_date, end_date
|
|
248
|
+
)
|
|
249
|
+
cohort_struct.quarterly_unknown_other_reason_outside_guidelines = unknown_other_reason_outside_guidelines(
|
|
250
|
+
quarter_start_date, end_date
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Children 12-23 months
|
|
254
|
+
|
|
255
|
+
# Unique PatientProgram entries at the current location for those patients with at least one state
|
|
256
|
+
# ON ARVs and earliest start date of the 'ON ARVs' state within the quarter and having
|
|
257
|
+
# Confirmed HIV Infection (HIV Rapid antibody test or DNA-PCR), regardless of WHO stage and CD4 Count
|
|
258
|
+
cohort_struct.children_12_59_months = children_12_59_months(start_date, end_date)
|
|
259
|
+
cohort_struct.cum_children_12_59_months = children_12_59_months(cum_start_date, end_date)
|
|
260
|
+
cohort_struct.quarterly_children_12_59_months = children_12_59_months(quarter_start_date, end_date)
|
|
261
|
+
|
|
262
|
+
# Current EPISODE OF TB
|
|
263
|
+
|
|
264
|
+
# Unique PatientProgram entries at the current location for those patients with at least one state
|
|
265
|
+
# ON ARVs and earliest start date of the 'ON ARVs' state within the quarter and having a
|
|
266
|
+
# CURRENT EPISODE OF TB observation at the HIV staging encounter on the initiation date
|
|
267
|
+
cohort_struct.current_episode_of_tb = current_episode_of_tb(start_date, end_date)
|
|
268
|
+
cohort_struct.cum_current_episode_of_tb = current_episode_of_tb(cum_start_date, end_date)
|
|
269
|
+
cohort_struct.quarterly_current_episode_of_tb = current_episode_of_tb(quarter_start_date, end_date)
|
|
270
|
+
|
|
271
|
+
# TB within the last 2 years
|
|
272
|
+
|
|
273
|
+
# Unique PatientProgram entries at the current location for those patients with at least one state ON ARVs
|
|
274
|
+
# and earliest start date of the 'ON ARVs' state within the quarter
|
|
275
|
+
# and having a TB WITHIN THE LAST 2 YEARS observation at the HIV staging encounter on the initiation date
|
|
276
|
+
cohort_struct.tb_within_the_last_two_years = tb_within_the_last_two_years(cohort_struct.current_episode_of_tb,
|
|
277
|
+
start_date, end_date)
|
|
278
|
+
cohort_struct.cum_tb_within_the_last_two_years = tb_within_the_last_two_years(
|
|
279
|
+
cohort_struct.cum_current_episode_of_tb, cum_start_date, end_date
|
|
280
|
+
)
|
|
281
|
+
cohort_struct.quarterly_tb_within_the_last_two_years = tb_within_the_last_two_years(
|
|
282
|
+
cohort_struct.quarterly_current_episode_of_tb, quarter_start_date, end_date
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# No TB
|
|
286
|
+
# total_registered - (current_episode - tb_within_the_last_two_years)
|
|
287
|
+
cohort_struct.no_tb = no_tb(cohort_struct.total_registered, cohort_struct.tb_within_the_last_two_years,
|
|
288
|
+
cohort_struct.current_episode_of_tb)
|
|
289
|
+
cohort_struct.cum_no_tb = cum_no_tb(cohort_struct.cum_total_registered,
|
|
290
|
+
cohort_struct.cum_tb_within_the_last_two_years, cohort_struct.cum_current_episode_of_tb)
|
|
291
|
+
cohort_struct.quarterly_no_tb = cum_no_tb(cohort_struct.quarterly_total_registered,
|
|
292
|
+
cohort_struct.quarterly_tb_within_the_last_two_years, cohort_struct.quarterly_current_episode_of_tb)
|
|
293
|
+
|
|
294
|
+
# Kaposis Sarcoma
|
|
295
|
+
#
|
|
296
|
+
# Unique PatientProgram entries at the current location for those patients with at least one state ON ARVs
|
|
297
|
+
# and earliest start date of the 'ON ARVs' state within the quarter and having a KAPOSIS SARCOMA observation
|
|
298
|
+
# at the HIV staging encounter on the initiation date
|
|
299
|
+
cohort_struct.kaposis_sarcoma = kaposis_sarcoma(start_date, end_date)
|
|
300
|
+
cohort_struct.cum_kaposis_sarcoma = kaposis_sarcoma(cum_start_date, end_date)
|
|
301
|
+
cohort_struct.quarterly_kaposis_sarcoma = kaposis_sarcoma(quarter_start_date, end_date)
|
|
302
|
+
|
|
303
|
+
# From this point going down: we update temp_earliest_start_date cum_outcome field to have the latest Cumulative outcome
|
|
304
|
+
update_cum_outcome(end_date)
|
|
305
|
+
update_tb_status(end_date)
|
|
306
|
+
update_patient_side_effects(end_date)
|
|
307
|
+
|
|
308
|
+
# Total Alive and On ART
|
|
309
|
+
# Unique PatientProgram entries at the current location for those patients with at least one state
|
|
310
|
+
# ON ARVs and earliest start date of the 'ON ARVs' state less than or equal to end date of quarter
|
|
311
|
+
# and latest state is ON ARVs (Excluding defaulters)
|
|
312
|
+
cohort_struct.total_alive_and_on_art = get_outcome('On antiretrovirals')
|
|
313
|
+
cohort_struct.died_within_the_1st_month_of_art_initiation = died_in('1st month')
|
|
314
|
+
cohort_struct.died_within_the_2nd_month_of_art_initiation = died_in('2nd month')
|
|
315
|
+
cohort_struct.died_within_the_3rd_month_of_art_initiation = died_in('3rd month')
|
|
316
|
+
cohort_struct.died_after_the_3rd_month_of_art_initiation = died_in('4+ months')
|
|
317
|
+
cohort_struct.died_total = get_outcome('Patient died')
|
|
318
|
+
cohort_struct.defaulted = get_outcome('Defaulted')
|
|
319
|
+
cohort_struct.stopped_art = get_outcome('Treatment stopped')
|
|
320
|
+
cohort_struct.transfered_out = get_outcome('Patient transferred out')
|
|
321
|
+
cohort_struct.unknown_outcome = get_outcome('Pre-ART (Continue)')
|
|
322
|
+
|
|
323
|
+
# ARV Regimen category
|
|
324
|
+
# Alive and On ART and Value Coded of the latest 'Regimen Category' Observation
|
|
325
|
+
# of each patient that is linked to the Dispensing encounter in the reporting period
|
|
326
|
+
|
|
327
|
+
prescriptions = cal_regimem_category(cohort_struct.total_alive_and_on_art, end_date)
|
|
328
|
+
|
|
329
|
+
# concepts = ->(names) { ::ConceptName.where(name: names).select(:concept_id) }
|
|
330
|
+
# drugs = ->(concepts) { ::Drug.where(concept: concepts).select(:drug_id).collect(&:drug_id) }
|
|
331
|
+
|
|
332
|
+
# lpv_granules = drugs[concepts[['LPV/r Pellets', 'LPV/r Granules']]]
|
|
333
|
+
# lpv_tabs = drugs[concepts['LPV/r']]
|
|
334
|
+
|
|
335
|
+
cohort_struct.zero_p = filter_prescriptions_by_regimen(prescriptions, '0P')
|
|
336
|
+
cohort_struct.zero_a = filter_prescriptions_by_regimen(prescriptions, '0A')
|
|
337
|
+
cohort_struct.two_p = filter_prescriptions_by_regimen(prescriptions, '2P')
|
|
338
|
+
cohort_struct.two_a = filter_prescriptions_by_regimen(prescriptions, '2A')
|
|
339
|
+
cohort_struct.four_a = filter_prescriptions_by_regimen(prescriptions, '4A')
|
|
340
|
+
cohort_struct.four_pp = filter_prescriptions_by_regimen(prescriptions, '4PP')
|
|
341
|
+
cohort_struct.four_pa = filter_prescriptions_by_regimen(prescriptions, '4PA')
|
|
342
|
+
cohort_struct.five_a = filter_prescriptions_by_regimen(prescriptions, '5A')
|
|
343
|
+
cohort_struct.six_a = filter_prescriptions_by_regimen(prescriptions, '6A')
|
|
344
|
+
cohort_struct.seven_a = filter_prescriptions_by_regimen(prescriptions, '7A')
|
|
345
|
+
cohort_struct.eight_a = filter_prescriptions_by_regimen(prescriptions, '8A')
|
|
346
|
+
cohort_struct.nine_a = filter_prescriptions_by_regimen(prescriptions, '9A')
|
|
347
|
+
cohort_struct.nine_pp = filter_prescriptions_by_regimen(prescriptions, '9PP')
|
|
348
|
+
cohort_struct.nine_pa = filter_prescriptions_by_regimen(prescriptions, '9PA')
|
|
349
|
+
cohort_struct.ten_a = filter_prescriptions_by_regimen(prescriptions, '10A')
|
|
350
|
+
cohort_struct.eleven_a = filter_prescriptions_by_regimen(prescriptions, '11A')
|
|
351
|
+
cohort_struct.eleven_pp = filter_prescriptions_by_regimen(prescriptions, '11PP')
|
|
352
|
+
cohort_struct.eleven_pa = filter_prescriptions_by_regimen(prescriptions, '11PA')
|
|
353
|
+
cohort_struct.twelve_a = filter_prescriptions_by_regimen(prescriptions, '12A')
|
|
354
|
+
cohort_struct.twelve_pp = filter_prescriptions_by_regimen(prescriptions, '12PP')
|
|
355
|
+
cohort_struct.twelve_pa = filter_prescriptions_by_regimen(prescriptions, '12PA')
|
|
356
|
+
cohort_struct.thirteen_a = filter_prescriptions_by_regimen(prescriptions, '13A')
|
|
357
|
+
cohort_struct.fourteen_pp = filter_prescriptions_by_regimen(prescriptions, '14PP')
|
|
358
|
+
cohort_struct.fourteen_pa = filter_prescriptions_by_regimen(prescriptions, '14PA')
|
|
359
|
+
cohort_struct.fourteen_a = filter_prescriptions_by_regimen(prescriptions, '14A')
|
|
360
|
+
cohort_struct.fifteen_pp = filter_prescriptions_by_regimen(prescriptions, '15PP')
|
|
361
|
+
cohort_struct.fifteen_pa = filter_prescriptions_by_regimen(prescriptions, '15PA')
|
|
362
|
+
cohort_struct.fifteen_a = filter_prescriptions_by_regimen(prescriptions, '15A')
|
|
363
|
+
cohort_struct.sixteen_p = filter_prescriptions_by_regimen(prescriptions, '16P')
|
|
364
|
+
cohort_struct.sixteen_a = filter_prescriptions_by_regimen(prescriptions, '16A')
|
|
365
|
+
cohort_struct.seventeen_pp = filter_prescriptions_by_regimen(prescriptions, '17PP')
|
|
366
|
+
cohort_struct.seventeen_pa = filter_prescriptions_by_regimen(prescriptions, '17PA')
|
|
367
|
+
cohort_struct.seventeen_a = filter_prescriptions_by_regimen(prescriptions, '17A')
|
|
368
|
+
cohort_struct.unknown_regimen = filter_prescriptions_by_regimen(prescriptions, 'unknown_regimen')
|
|
369
|
+
|
|
370
|
+
# Total patients with side effects:
|
|
371
|
+
# Alive and On ART patients with DRUG INDUCED observations during their last HIV CLINIC CONSULTATION encounter up to the reporting period
|
|
372
|
+
|
|
373
|
+
with_se, without_se, se_unknowns = patients_side_effects_status(cohort_struct.total_alive_and_on_art, end_date)
|
|
374
|
+
cohort_struct.total_patients_with_side_effects = with_se
|
|
375
|
+
cohort_struct.total_patients_without_side_effects = without_se
|
|
376
|
+
cohort_struct.unknown_side_effects = se_unknowns
|
|
377
|
+
|
|
378
|
+
# TB Status
|
|
379
|
+
# Alive and On ART with 'TB Status' observation value of 'TB not Suspected' or 'TB Suspected'
|
|
380
|
+
# or 'TB confirmed and on Treatment', or 'TB confirmed and not on Treatment' or 'Unknown TB status'
|
|
381
|
+
# during their latest HIV Clinic Consultaiton encounter in the reporting period
|
|
382
|
+
write_tb_status_indicators(cohort_struct, cohort_struct.total_alive_and_on_art, start_date, end_date)
|
|
383
|
+
|
|
384
|
+
# ART adherence
|
|
385
|
+
#
|
|
386
|
+
# Alive and On ART with value of their 'Drug order adherence" observation during their latest Adherence
|
|
387
|
+
# encounter in the reporting period between 95 and 105
|
|
388
|
+
adherent, not_adherent, unknown_adherence = latest_art_adherence(cohort_struct.total_alive_and_on_art,
|
|
389
|
+
start_date, end_date)
|
|
390
|
+
cohort_struct.patients_with_0_6_doses_missed_at_their_last_visit = adherent
|
|
391
|
+
cohort_struct.patients_with_7_plus_doses_missed_at_their_last_visit = not_adherent
|
|
392
|
+
cohort_struct.patients_with_unknown_adhrence = unknown_adherence
|
|
393
|
+
|
|
394
|
+
# Pregnant and breastfeeding status during Consultaiton
|
|
395
|
+
cohort_struct.total_pregnant_women = total_pregnant_women(cohort_struct.total_alive_and_on_art, start_date,
|
|
396
|
+
end_date)
|
|
397
|
+
cohort_struct.total_breastfeeding_women = total_breastfeeding_women(cohort_struct.total_alive_and_on_art,
|
|
398
|
+
cohort_struct.total_pregnant_women, start_date, end_date)
|
|
399
|
+
cohort_struct.total_other_patients = total_other_patients(cohort_struct.total_alive_and_on_art,
|
|
400
|
+
cohort_struct.total_breastfeeding_women, cohort_struct.total_pregnant_women)
|
|
401
|
+
|
|
402
|
+
# Patients with CPT dispensed at least once before end of quarter and on ARVs
|
|
403
|
+
cohort_struct.total_patients_on_arvs_and_cpt = total_patients_on_arvs_and_cpt(
|
|
404
|
+
cohort_struct.total_alive_and_on_art, start_date, end_date
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
# Patients with IPT dispensed at least once before end of quarter and on ARVS
|
|
408
|
+
cohort_struct.total_patients_on_arvs_and_ipt = total_patients_on_arvs_and_ipt(
|
|
409
|
+
cohort_struct.total_alive_and_on_art, start_date, end_date
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
# Patients on family planning methods at least once before end of quarter and on ARVs
|
|
413
|
+
cohort_struct.total_patients_on_family_planning = total_patients_on_family_planning(
|
|
414
|
+
cohort_struct.total_alive_and_on_art, quarter_start_date, end_date
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
# Patients whose BP was screened and are above 30 years least once before end of quarter and on ARVs
|
|
418
|
+
cohort_struct.total_patients_with_screened_bp = total_patients_with_screened_bp(
|
|
419
|
+
total_patients_alive_and_on_art_above_30_years(cohort_struct.total_alive_and_on_art,
|
|
420
|
+
end_date), start_date, end_date
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
# Patients who started TPT in current reporting period
|
|
424
|
+
tpt = MalawiHivProgramReports::Cohort::Tpt.new(start_date:, end_date:, occupation:,
|
|
425
|
+
location: @location)
|
|
426
|
+
# tpt newly initiated has a property last_tpt_start_date we need to use that to get those clients
|
|
427
|
+
cohort_struct.newly_initiated_on_3hp = tpt.newly_initiated_on_3hp.select do |hash|
|
|
428
|
+
hash['last_tpt_start_date'].nil?
|
|
429
|
+
end
|
|
430
|
+
cohort_struct.newly_initiated_on_ipt = tpt.newly_initiated_on_ipt.select do |hash|
|
|
431
|
+
hash['last_tpt_start_date'].nil?
|
|
432
|
+
end
|
|
433
|
+
time_ended = Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
|
434
|
+
puts "Started at: #{time_started}. Finished at: #{time_ended}. Total time in minutes: #{(Time.parse(time_ended) - Time.parse(time_started)) / 60}"
|
|
435
|
+
Rails.logger.info "Started at: #{time_started}. Finished at: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}. Total time in minutes: #{(Time.parse(time_ended) - Time.parse(time_started)) / 60}"
|
|
436
|
+
cohort_struct
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
# private
|
|
440
|
+
|
|
441
|
+
def get_disaggregated_cohort(start_date, end_date, gender, ag)
|
|
442
|
+
case ag
|
|
443
|
+
when '50+ years'
|
|
444
|
+
diff = [50, 1000]
|
|
445
|
+
iu = 'year'
|
|
446
|
+
when /years/i
|
|
447
|
+
diff = ag.sub(' years', '').split('-')
|
|
448
|
+
iu = 'year'
|
|
449
|
+
when /months/i
|
|
450
|
+
diff = ag.sub(' months', '').split('-')
|
|
451
|
+
iu = 'month'
|
|
452
|
+
else
|
|
453
|
+
case gender
|
|
454
|
+
when 'M'
|
|
455
|
+
diff = [0, 1000]
|
|
456
|
+
iu = 'year'
|
|
457
|
+
gender = 'M'
|
|
458
|
+
when 'FNP'
|
|
459
|
+
diff = [0, 1000]
|
|
460
|
+
iu = 'year'
|
|
461
|
+
gender = 'F'
|
|
462
|
+
when 'FP'
|
|
463
|
+
diff = [0, 1000]
|
|
464
|
+
iu = 'year'
|
|
465
|
+
gender = 'F'
|
|
466
|
+
when 'FBf'
|
|
467
|
+
diff = [0, 1000]
|
|
468
|
+
iu = 'year'
|
|
469
|
+
gender = 'F'
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
data = ActiveRecord::Base.connection.select_all(
|
|
474
|
+
"SELECT patient_id FROM temp_earliest_start_date
|
|
475
|
+
WHERE earliest_start_date BETWEEN '#{start_date.to_date}' AND '#{end_date.to_date}'
|
|
476
|
+
AND (earliest_start_date) = (date_enrolled) AND gender = '#{gender.first}' #{site_manager(operator: 'AND',
|
|
477
|
+
column: 'site_id', location: @location)}
|
|
478
|
+
AND timestampdiff(#{iu}, birthdate, date_enrolled) BETWEEN #{diff[0].to_i} AND #{diff[1].to_i}"
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
data1 = ActiveRecord::Base.connection.select_all(
|
|
482
|
+
"SELECT t1.patient_id
|
|
483
|
+
FROM temp_earliest_start_date t1
|
|
484
|
+
INNER JOIN temp_patient_outcomes t2 ON t1.patient_id = t2.patient_id #{site_manager(operator: 'AND',
|
|
485
|
+
column: 't2.site_id', location: @location)}
|
|
486
|
+
WHERE date_enrolled <= '#{end_date.to_date}' AND gender = '#{gender.first}'
|
|
487
|
+
AND cum_outcome = 'On antiretrovirals' #{site_manager(operator: 'AND', column: 't1.site_id',
|
|
488
|
+
location: @location)}
|
|
489
|
+
AND timestampdiff(#{iu}, birthdate, date_enrolled) BETWEEN #{diff[0].to_i} AND #{diff[1].to_i}"
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
::EncounterType.find_by_name('DISPENSING').id
|
|
493
|
+
amount_dispensed = concept('Amount dispensed').concept_id
|
|
494
|
+
ipt_drug_ids = ::Drug.find_all_by_concept_id(656).map(&:drug_id)
|
|
495
|
+
|
|
496
|
+
patient_ids = []
|
|
497
|
+
(data1 || {}).each_key do |x|
|
|
498
|
+
patient_ids << x['patient_id'].to_i
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
unless patient_ids.blank?
|
|
502
|
+
data2 = ActiveRecord::Base.connection.select_all(
|
|
503
|
+
"SELECT e.patient_id
|
|
504
|
+
FROM encounter e
|
|
505
|
+
INNER JOIN temp_patient_outcomes o ON o.patient_id = e.patient_id
|
|
506
|
+
AND o.cum_outcome = 'On antiretrovirals' #{site_manager(operator: 'AND', column: 'o.site_id',
|
|
507
|
+
location: @location)}
|
|
508
|
+
INNER JOIN obs ON obs.encounter_id = e.encounter_id
|
|
509
|
+
AND obs.concept_id = #{amount_dispensed} #{site_manager(operator: 'AND', column: 'obs.site_id',
|
|
510
|
+
location: @location)}
|
|
511
|
+
WHERE value_drug IN(#{ipt_drug_ids.join(',')})
|
|
512
|
+
AND e.patient_id IN(#{patient_ids.join(',')})
|
|
513
|
+
AND e.encounter_datetime BETWEEN '#{start_date.to_date.strftime('%Y-%m-%d 00:00:00')}'
|
|
514
|
+
AND '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}' #{site_manager(operator: 'AND',
|
|
515
|
+
column: 'e.site_id', location: @location)}
|
|
516
|
+
GROUP BY e.patient_id"
|
|
517
|
+
)
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
[data&.length || 0, data1&.length || 0, data2.length || 0, 0]
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def patient_with_missing_start_reasons(start_date, end_date)
|
|
524
|
+
art_patients = ActiveRecord::Base.connection.select_all(
|
|
525
|
+
"SELECT e.*, #{function_manager(function: 'patient_reason_for_starting_art_text', location: @location,
|
|
526
|
+
args: "e.patient_id, #{@location}")} reason
|
|
527
|
+
FROM temp_earliest_start_date e
|
|
528
|
+
WHERE date_enrolled BETWEEN '#{start_date.to_date}' AND '#{end_date.to_date}' #{site_manager(operator: 'AND',
|
|
529
|
+
column: 'e.site_id', location: @location)}"
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
data = {}
|
|
533
|
+
art_patients.each do |p|
|
|
534
|
+
patient = ::Patient.find(p['patient_id'].to_i)
|
|
535
|
+
reason_for_starting = p['reason']
|
|
536
|
+
next unless reason_for_starting.blank?
|
|
537
|
+
|
|
538
|
+
data[patient.patient_id] = {
|
|
539
|
+
arv_number: patient.arv_number,
|
|
540
|
+
earliest_start_date: (begin
|
|
541
|
+
p['earliest_start_date'].to_date
|
|
542
|
+
rescue StandardError
|
|
543
|
+
nil
|
|
544
|
+
end),
|
|
545
|
+
date_enrolled: (begin
|
|
546
|
+
p['date_enrolled'].to_date
|
|
547
|
+
rescue StandardError
|
|
548
|
+
nil
|
|
549
|
+
end),
|
|
550
|
+
name: patient.person.name,
|
|
551
|
+
gender: patient.person.gender,
|
|
552
|
+
birthdate: patient.person.birth_date,
|
|
553
|
+
outcome: p['outcome']
|
|
554
|
+
}
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
data
|
|
558
|
+
rescue StandardError
|
|
559
|
+
raise 'Try running the revised cohort before this report'
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def on_art_patients_with_no_arvs_dispensations(start_date, end_date)
|
|
563
|
+
arv_drugs = MedicationService.arv_drugs
|
|
564
|
+
arv_drugs = arv_drugs.map(&:concept_id)
|
|
565
|
+
|
|
566
|
+
start_date.to_date
|
|
567
|
+
end_date.to_date
|
|
568
|
+
|
|
569
|
+
data = ActiveRecord::Base.connection.select_all(
|
|
570
|
+
"SELECT patient_id
|
|
571
|
+
FROM orders o
|
|
572
|
+
INNER JOIN drug_order drg ON drg.order_id = o.order_id #{site_manager(operator: 'AND', column: 'drg.site_id',
|
|
573
|
+
location: @location)}
|
|
574
|
+
AND o.voided = 0
|
|
575
|
+
WHERE drug_inventory_id IN(
|
|
576
|
+
SELECT drug_id FROM drug
|
|
577
|
+
WHERE concept_id IN(#{arv_drugs.join(',')})
|
|
578
|
+
) #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
|
579
|
+
GROUP BY patient_id"
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
patient_ids = data.map { |d| d['patient_id'].to_i }
|
|
583
|
+
|
|
584
|
+
begin
|
|
585
|
+
patients = ActiveRecord::Base.connection.select_all(
|
|
586
|
+
"SELECT * FROM temp_earliest_start_date WHERE patient_id NOT IN(#{patient_ids.join(',')}) #{site_manager(
|
|
587
|
+
operator: 'AND', column: 'site_id', location: @location
|
|
588
|
+
)}"
|
|
589
|
+
)
|
|
590
|
+
rescue StandardError
|
|
591
|
+
raise 'Try running the revised cohort before this report'
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
reason_for_starting = concept('REASON FOR ART ELIGIBILITY')
|
|
595
|
+
data = {}
|
|
596
|
+
|
|
597
|
+
(patients || []).each do |p|
|
|
598
|
+
patient = ::Patient.find(p['patient_id'].to_i)
|
|
599
|
+
reason_for_starting = ::PatientService.reason_for_art_eligibility(patient)
|
|
600
|
+
# next unless reason_for_starting.blank?
|
|
601
|
+
|
|
602
|
+
patient_obj = ::PatientService.get_patient(patient.person)
|
|
603
|
+
data[patient_obj.patient_id] = {
|
|
604
|
+
arv_number: patient_obj.arv_number,
|
|
605
|
+
earliest_start_date: p['earliest_start_date'],
|
|
606
|
+
date_enrolled: p['date_enrolled'].to_date,
|
|
607
|
+
name: patient_obj.name,
|
|
608
|
+
gender: patient_obj.sex,
|
|
609
|
+
birthdate: patient_obj.birth_date,
|
|
610
|
+
outcome: p['outcome']
|
|
611
|
+
}
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
data
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
def patients_with_pre_art_or_unknown_outcome(_start_date, _end_date)
|
|
618
|
+
begin
|
|
619
|
+
patients = ActiveRecord::Base.connection.select_all(
|
|
620
|
+
"SELECT e.*, cum_outcome, #{function_manager(function: 'patient_reason_for_starting_art_text',
|
|
621
|
+
location: @location, args: "e.patient_id, #{@location}")} reason_for_starting
|
|
622
|
+
FROM temp_patient_outcomes o
|
|
623
|
+
INNER JOIN temp_earliest_start_date e ON e.patient_id = o.patient_id #{site_manager(operator: 'AND',
|
|
624
|
+
column: 'e.site_id', location: @location)}
|
|
625
|
+
WHERE cum_outcome LIKE '%Pre-%' OR cum_outcome LIKE '%Unknown%' #{site_manager(operator: 'AND',
|
|
626
|
+
column: 'o.site_id', location: @location)}"
|
|
627
|
+
)
|
|
628
|
+
rescue StandardError
|
|
629
|
+
raise 'Try running the revised cohort before this report'
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
data = {}
|
|
633
|
+
|
|
634
|
+
(patients || []).each do |p|
|
|
635
|
+
::Patient.find(p['patient_id'].to_i)
|
|
636
|
+
|
|
637
|
+
patient_outcome = p['cum_outcome']
|
|
638
|
+
person = ::Person.find(p['patient_id'])
|
|
639
|
+
|
|
640
|
+
patient_obj = ::PatientService.get_patient(person)
|
|
641
|
+
data[patient_obj.patient_id] = {
|
|
642
|
+
arv_number: patient_obj.arv_number,
|
|
643
|
+
earliest_start_date: (begin
|
|
644
|
+
p['earliest_start_date'].to_date
|
|
645
|
+
rescue StandardError
|
|
646
|
+
nil
|
|
647
|
+
end),
|
|
648
|
+
date_enrolled: (begin
|
|
649
|
+
p['date_enrolled'].to_date
|
|
650
|
+
rescue StandardError
|
|
651
|
+
nil
|
|
652
|
+
end),
|
|
653
|
+
name: patient_obj.name,
|
|
654
|
+
gender: patient_obj.sex,
|
|
655
|
+
birthdate: patient_obj.birth_date,
|
|
656
|
+
reason_for_starting: p['reason_for_starting'],
|
|
657
|
+
outcome: patient_outcome['outcome']
|
|
658
|
+
}
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
data
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
STATE_DIED = 3
|
|
665
|
+
STATE_ON_TREATMENT = 7
|
|
666
|
+
|
|
667
|
+
def load_data_into_temp_earliest_start_date(end_date, occupation = nil)
|
|
668
|
+
load_data_into_temp_cohort_members_table(end_date)
|
|
669
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
670
|
+
INSERT INTO temp_earliest_start_date
|
|
671
|
+
SELECT patient_id, site_id, date_enrolled, earliest_start_date, recorded_start_date, birthdate, birthdate_estimated, death_date, gender, age_at_initiation, age_in_days, reason_for_starting_art
|
|
672
|
+
FROM temp_cohort_members #{occupation_filter(occupation:, field_name: 'occupation')} #{site_manager(operator: min_filt(occupation).to_s, column: 'site_id', location: @location)}
|
|
673
|
+
SQL
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
# rubocop:disable Metrics/MethodLength
|
|
677
|
+
def load_data_into_temp_cohort_members_table(end_date)
|
|
678
|
+
end_date = ActiveRecord::Base.connection.quote(end_date)
|
|
679
|
+
|
|
680
|
+
type_of_patient_concept = concept('Type of patient').concept_id
|
|
681
|
+
new_patient_concept = concept('New patient').concept_id
|
|
682
|
+
drug_refill_concept = concept('Drug refill').concept_id
|
|
683
|
+
external_concept = concept('External Consultation').concept_id
|
|
684
|
+
program_id = program('HIV program').id
|
|
685
|
+
|
|
686
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
687
|
+
INSERT INTO temp_cohort_members
|
|
688
|
+
SELECT patient_program.patient_id, #{@location} as site_id,
|
|
689
|
+
DATE(MIN(art_order.start_date)) AS date_enrolled,
|
|
690
|
+
DATE(COALESCE(MIN(art_start_date_obs.value_datetime), MIN(art_order.start_date))) AS earliest_start_date,
|
|
691
|
+
DATE(MIN(art_start_date_obs.value_datetime)) AS recorded_start_date,
|
|
692
|
+
person.birthdate,
|
|
693
|
+
#{@adapter == 'mysql2' ? 'person.birthdate_estimated' : '(CASE WHEN person.birthdate_estimated = 0 THEN false ELSE true END)'} AS birthdate_estimated,
|
|
694
|
+
person.death_date,
|
|
695
|
+
person.gender, #{
|
|
696
|
+
if @adapter == 'mysql2'
|
|
697
|
+
<<~SQL
|
|
698
|
+
IF(person.birthdate IS NOT NULL, TIMESTAMPDIFF(YEAR, person.birthdate, DATE(COALESCE(art_start_date_obs.value_datetime, MIN(art_order.start_date)))), NULL) AS age_at_initiation,
|
|
699
|
+
IF(person.birthdate IS NOT NULL, TIMESTAMPDIFF(DAY, person.birthdate, DATE(COALESCE(art_start_date_obs.value_datetime, MIN(art_order.start_date)))), NULL) AS age_in_days,
|
|
700
|
+
SQL
|
|
701
|
+
else
|
|
702
|
+
<<~SQL
|
|
703
|
+
CASE
|
|
704
|
+
WHEN person.birthdate IS NOT NULL THEN
|
|
705
|
+
EXTRACT(YEAR FROM AGE(COALESCE(MIN(art_start_date_obs.value_datetime)::DATE, MIN(art_order.start_date)), person.birthdate))
|
|
706
|
+
ELSE NULL
|
|
707
|
+
END AS age_at_initiation,
|
|
708
|
+
CASE
|
|
709
|
+
WHEN person.birthdate IS NOT NULL THEN
|
|
710
|
+
EXTRACT(DAY FROM AGE(COALESCE(MIN(art_start_date_obs.value_datetime)::DATE, MIN(art_order.start_date)), person.birthdate))
|
|
711
|
+
ELSE NULL
|
|
712
|
+
END AS age_in_days,
|
|
713
|
+
SQL
|
|
714
|
+
end
|
|
715
|
+
}
|
|
716
|
+
(SELECT value_coded FROM obs
|
|
717
|
+
WHERE concept_id = 7563 AND person_id = patient_program.patient_id AND voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
718
|
+
ORDER BY obs_datetime DESC, date_created DESC LIMIT 1) as reason_for_starting_art,
|
|
719
|
+
pa.value AS occupation
|
|
720
|
+
FROM patient_program
|
|
721
|
+
INNER JOIN person ON person.person_id = patient_program.patient_id #{site_manager(operator: 'AND', column: 'person.site_id', location: @location)}
|
|
722
|
+
LEFT JOIN (#{current_occupation_query}) pa ON pa.person_id = patient_program.patient_id #{site_manager(operator: 'AND', column: 'pa.site_id', location: @location)}
|
|
723
|
+
LEFT JOIN patient_state AS outcome
|
|
724
|
+
ON outcome.patient_program_id = patient_program.patient_program_id #{site_manager(operator: 'AND', column: 'outcome.site_id', location: @location)}
|
|
725
|
+
LEFT JOIN encounter AS clinic_registration_encounter
|
|
726
|
+
ON clinic_registration_encounter.encounter_type = (
|
|
727
|
+
SELECT encounter_type_id FROM encounter_type WHERE LOWER(name) = LOWER('HIV CLINIC REGISTRATION') LIMIT 1
|
|
728
|
+
)
|
|
729
|
+
AND clinic_registration_encounter.patient_id = patient_program.patient_id
|
|
730
|
+
AND clinic_registration_encounter.program_id = patient_program.program_id
|
|
731
|
+
AND clinic_registration_encounter.encounter_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')}
|
|
732
|
+
AND clinic_registration_encounter.voided = 0 #{site_manager(operator: 'AND', column: 'clinic_registration_encounter.site_id', location: @location)}
|
|
733
|
+
LEFT JOIN obs AS art_start_date_obs
|
|
734
|
+
ON art_start_date_obs.concept_id = 2516
|
|
735
|
+
AND art_start_date_obs.person_id = patient_program.patient_id
|
|
736
|
+
AND art_start_date_obs.voided = 0 #{site_manager(operator: 'AND', column: 'art_start_date_obs.site_id', location: @location)}
|
|
737
|
+
AND art_start_date_obs.obs_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')}
|
|
738
|
+
AND art_start_date_obs.encounter_id = clinic_registration_encounter.encounter_id
|
|
739
|
+
/* TODO: Re-enable the following condition. Has been removed because LLH and PIH
|
|
740
|
+
were noted to be dropping patients because of it. Seems these sites may have orders
|
|
741
|
+
without corresponding encounters. Adding this condition bumps up performance a bit. */
|
|
742
|
+
/* INNER JOIN encounter AS prescription_encounter
|
|
743
|
+
ON prescription_encounter.patient_id = patient_program.patient_id
|
|
744
|
+
AND prescription_encounter.program_id = patient_program.program_id
|
|
745
|
+
AND prescription_encounter.encounter_datetime < DATE(#{end_date}) + INTERVAL 1 DAY
|
|
746
|
+
AND prescription_encounter.encounter_type IN (SELECT encounter_type_id FROM encounter_type WHERE name LIKE 'Treatment')
|
|
747
|
+
AND prescription_encounter.voided = 0 */
|
|
748
|
+
LEFT JOIN temp_register_start_date AS patient_type_obs
|
|
749
|
+
ON patient_type_obs.patient_id = patient_program.patient_id #{site_manager(operator: 'AND', column: 'patient_type_obs.site_id', location: @location)}
|
|
750
|
+
/*INNER JOIN orders AS art_order
|
|
751
|
+
ON art_order.patient_id = patient_program.patient_id
|
|
752
|
+
-- AND art_order.encounter_id = prescription_encounter.encounter_id
|
|
753
|
+
AND art_order.concept_id IN (SELECT concept_id FROM concept_set WHERE concept_set = 1085)
|
|
754
|
+
AND art_order.start_date < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')}
|
|
755
|
+
AND art_order.order_type_id = #{order_type('Drug order').id}
|
|
756
|
+
AND art_order.start_date >= COALESCE(patient_type_obs.start_date, DATE('1901-01-01'))
|
|
757
|
+
AND art_order.voided = 0 #{site_manager(operator: 'AND', column: 'art_order.site_id', location: @location)}
|
|
758
|
+
INNER JOIN drug_order
|
|
759
|
+
ON drug_order.order_id = art_order.order_id
|
|
760
|
+
AND drug_order.quantity > 0 #{site_manager(operator: 'AND', column: 'drug_order.site_id', location: @location)} */
|
|
761
|
+
INNER JOIN temp_order_details AS art_order ON art_order.patient_id = patient_program.patient_id #{site_manager(operator: 'AND', column: 'art_order.site_id', location: @location)}
|
|
762
|
+
WHERE patient_program.voided = 0
|
|
763
|
+
AND outcome.voided = 0
|
|
764
|
+
AND patient_program.program_id = 1
|
|
765
|
+
AND outcome.state = 7
|
|
766
|
+
AND outcome.start_date IS NOT NULL #{site_manager(operator: 'AND', column: 'patient_program.site_id', location: @location)}
|
|
767
|
+
/*AND patient_program.patient_id NOT IN (
|
|
768
|
+
SELECT e.patient_id FROM encounter e
|
|
769
|
+
LEFT JOIN (SELECT * FROM obs WHERE concept_id = #{type_of_patient_concept} AND voided = 0 AND value_coded = #{new_patient_concept}) AS new_patient ON e.patient_id = new_patient.person_id
|
|
770
|
+
LEFT JOIN (SELECT * FROM obs WHERE concept_id = #{type_of_patient_concept} AND voided = 0 AND value_coded = #{drug_refill_concept}) AS refill ON e.patient_id = refill.person_id
|
|
771
|
+
LEFT JOIN (SELECT * FROM obs WHERE concept_id = #{type_of_patient_concept} AND voided = 0 AND value_coded = #{external_concept}) AS external ON e.patient_id = external.person_id
|
|
772
|
+
WHERE e.program_id = #{program_id} AND (refill.value_coded IS NOT NULL OR external.value_coded IS NOT NULL)
|
|
773
|
+
AND new_patient.value_coded IS NULL
|
|
774
|
+
AND e.encounter_datetime < DATE(#{end_date}) + INTERVAL 1 DAY
|
|
775
|
+
AND e.encounter_type IN (SELECT encounter_type_id FROM encounter_type WHERE name = 'REGISTRATION' AND retired = 0)
|
|
776
|
+
GROUP BY e.patient_id
|
|
777
|
+
)*/
|
|
778
|
+
GROUP by patient_program.patient_id #{@adapter == 'mysql2' ? '' : ',person.birthdate, person.birthdate_estimated, person.death_date, person.gender, pa.value'}
|
|
779
|
+
HAVING #{@adapter == 'mysql2' ? 'date_enrolled' : 'art_order.start_date'} <= #{end_date}
|
|
780
|
+
SQL
|
|
781
|
+
remove_drug_refills_and_external_consultation(end_date)
|
|
782
|
+
end
|
|
783
|
+
# rubocop:enable Metrics/MethodLength
|
|
784
|
+
|
|
785
|
+
##
|
|
786
|
+
# This will hold crucial information for cohort members
|
|
787
|
+
# rubocop:disable Metrics/MethodLength
|
|
788
|
+
# rubocop:disable Metrics/AbcSize
|
|
789
|
+
def create_temp_cohort_members_table
|
|
790
|
+
ActiveRecord::Base.connection.execute('DROP TABLE IF EXISTS temp_cohort_members')
|
|
791
|
+
exe_temp_cohort_members_table(adapter: @adapter)
|
|
792
|
+
|
|
793
|
+
ActiveRecord::Base.connection.execute(
|
|
794
|
+
'CREATE INDEX member_id_index ON temp_cohort_members (patient_id)'
|
|
795
|
+
)
|
|
796
|
+
ActiveRecord::Base.connection.execute(
|
|
797
|
+
'CREATE INDEX member_site_index ON temp_cohort_members (site_id)'
|
|
798
|
+
)
|
|
799
|
+
ActiveRecord::Base.connection.execute(
|
|
800
|
+
'CREATE INDEX member_id_site_index ON temp_cohort_members (patient_id, site_id)'
|
|
801
|
+
)
|
|
802
|
+
ActiveRecord::Base.connection.execute(
|
|
803
|
+
'CREATE INDEX member_enrolled_index ON temp_cohort_members (date_enrolled)'
|
|
804
|
+
)
|
|
805
|
+
ActiveRecord::Base.connection.execute(
|
|
806
|
+
'CREATE INDEX member_date_enrolled_index ON temp_cohort_members (patient_id, date_enrolled)'
|
|
807
|
+
)
|
|
808
|
+
ActiveRecord::Base.connection.execute(
|
|
809
|
+
'CREATE INDEX member_start_date_index ON temp_cohort_members (earliest_start_date)'
|
|
810
|
+
)
|
|
811
|
+
ActiveRecord::Base.connection.execute(
|
|
812
|
+
'CREATE INDEX member_start_date__date_enrolled_index ON temp_cohort_members (patient_id, earliest_start_date, date_enrolled, gender)'
|
|
813
|
+
)
|
|
814
|
+
ActiveRecord::Base.connection.execute(
|
|
815
|
+
'CREATE INDEX member_reason ON temp_cohort_members (reason_for_starting_art)'
|
|
816
|
+
)
|
|
817
|
+
ActiveRecord::Base.connection.execute(
|
|
818
|
+
'CREATE INDEX member_birthdate_idx ON temp_cohort_members (birthdate)'
|
|
819
|
+
)
|
|
820
|
+
ActiveRecord::Base.connection.execute(
|
|
821
|
+
'CREATE INDEX member_occupation_idx ON temp_cohort_members (birthdate)'
|
|
822
|
+
)
|
|
823
|
+
end
|
|
824
|
+
# rubocop:enable Metrics/AbcSize
|
|
825
|
+
# rubocop:enable Metrics/MethodLength
|
|
826
|
+
|
|
827
|
+
def create_temp_other_patient_types(_end_date)
|
|
828
|
+
type_of_patient_concept = concept('Type of patient').concept_id
|
|
829
|
+
drug_refill_concept = concept('Drug refill').concept_id
|
|
830
|
+
external_concept = concept('External consultation').concept_id
|
|
831
|
+
exe_temp_other_patient_types(adapter: @adapter)
|
|
832
|
+
|
|
833
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
834
|
+
INSERT INTO temp_other_patient_types (patient_id, site_id)
|
|
835
|
+
SELECT pp.patient_id as patient_id, pp.site_id as site_id
|
|
836
|
+
FROM patient_program pp
|
|
837
|
+
INNER JOIN obs o ON pp.patient_id = o.person_id AND o.concept_id = #{type_of_patient_concept} #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
|
838
|
+
AND #{in_manager(column: 'o.value_coded', values: [drug_refill_concept, external_concept])}
|
|
839
|
+
AND o.voided = 0
|
|
840
|
+
WHERE pp.program_id = 1
|
|
841
|
+
AND pp.voided = 0
|
|
842
|
+
#{site_manager(operator: 'AND', column: 'pp.site_id', location: @location)}
|
|
843
|
+
GROUP BY pp.patient_id #{@adapter == 'mysql2' ? '' : ', pp.site_id'}
|
|
844
|
+
SQL
|
|
845
|
+
end
|
|
846
|
+
|
|
847
|
+
def drop_temp_other_patient_types
|
|
848
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
849
|
+
DROP TABLE IF EXISTS temp_other_patient_types
|
|
850
|
+
SQL
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
def drop_temp_order_details
|
|
854
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
855
|
+
DROP TABLE IF EXISTS temp_order_details
|
|
856
|
+
SQL
|
|
857
|
+
end
|
|
858
|
+
|
|
859
|
+
def create_temp_order_details(end_date)
|
|
860
|
+
exe_temp_order_details_table(adapter: @adapter)
|
|
861
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
862
|
+
INSERT INTO temp_order_details
|
|
863
|
+
SELECT o.patient_id, o.site_id, DATE(MIN(o.start_date)) start_date#{' '}
|
|
864
|
+
FROM orders o
|
|
865
|
+
INNER JOIN drug_order do ON do.order_id = o.order_id AND do.quantity > 0 #{site_manager(operator: 'AND', column: 'do.site_id', location: @location)}
|
|
866
|
+
LEFT JOIN temp_register_start_date trsd ON trsd.patient_id = o.patient_id #{site_manager(operator: 'AND', column: 'trsd.site_id', location: @location)}
|
|
867
|
+
WHERE o.concept_id IN (SELECT concept_id FROM concept_set WHERE concept_set = 1085)
|
|
868
|
+
AND o.start_date < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')} AND o.start_date >= COALESCE(trsd.start_date, DATE('1901-01-01'))
|
|
869
|
+
and o.order_type_id = 1 ANd o.voided = 0 #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
|
870
|
+
GROUP BY o.patient_id;
|
|
871
|
+
SQL
|
|
872
|
+
end
|
|
873
|
+
|
|
874
|
+
def create_temp_register_start_date_table(_end_date)
|
|
875
|
+
type_of_patient_concept = concept('Type of patient').concept_id
|
|
876
|
+
new_patient_concept = concept('New patient').concept_id
|
|
877
|
+
exe_temp_register_start_date_table(adapter: @adapter)
|
|
878
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
|
879
|
+
INSERT INTO temp_register_start_date (patient_id, start_date, site_id)
|
|
880
|
+
SELECT pp.patient_id as patient_id, MIN(o.obs_datetime) AS start_date, pp.site_id as site_id
|
|
881
|
+
FROM patient_program pp
|
|
882
|
+
INNER JOIN temp_other_patient_types tmp ON tmp.patient_id = pp.patient_id
|
|
883
|
+
#{site_manager(operator: 'AND', column: 'pp.site_id', location: @location)}
|
|
884
|
+
INNER JOIN obs o ON pp.patient_id = o.person_id AND o.concept_id = #{type_of_patient_concept}
|
|
885
|
+
#{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
|
886
|
+
AND o.value_coded = #{new_patient_concept}
|
|
887
|
+
AND o.voided = 0
|
|
888
|
+
WHERE pp.program_id = 1
|
|
889
|
+
#{site_manager(operator: 'AND', column: 'pp.site_id', location: @location)}
|
|
890
|
+
GROUP BY pp.patient_id #{@adapter == 'mysql2' ? '' : ', pp.site_id'}
|
|
891
|
+
SQL
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
def drop_temp_register_start_date_table
|
|
895
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
896
|
+
DROP TABLE IF EXISTS temp_register_start_date
|
|
897
|
+
SQL
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
def remove_drug_refills_and_external_consultation(end_date)
|
|
901
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
902
|
+
DELETE FROM temp_cohort_members
|
|
903
|
+
WHERE #{in_manager(column: 'patient_id', values: drug_refills_and_external_consultation_list(end_date))}
|
|
904
|
+
SQL
|
|
905
|
+
end
|
|
906
|
+
|
|
907
|
+
# this just gives all clients who are truly external or drug refill
|
|
908
|
+
# rubocop:disable Metrics/MethodLength
|
|
909
|
+
# rubocop:disable Metrics/AbcSize
|
|
910
|
+
def drug_refills_and_external_consultation_list(end_date)
|
|
911
|
+
to_remove = [0]
|
|
912
|
+
|
|
913
|
+
type_of_patient_concept = concept('Type of patient').concept_id
|
|
914
|
+
new_patient_concept = concept('New patient').concept_id
|
|
915
|
+
drug_refill_concept = concept('Drug refill').concept_id
|
|
916
|
+
external_concept = concept('External consultation').concept_id
|
|
917
|
+
hiv_clinic_registration_id = ::EncounterType.find_by_name('HIV CLINIC REGISTRATION').encounter_type_id
|
|
918
|
+
|
|
919
|
+
ActiveRecord::Base.connection.select_all("SELECT e.patient_id
|
|
920
|
+
FROM temp_cohort_members e
|
|
921
|
+
LEFT JOIN encounter as hiv_registration ON hiv_registration.patient_id = e.patient_id
|
|
922
|
+
AND hiv_registration.encounter_datetime < DATE(#{end_date})
|
|
923
|
+
AND hiv_registration.encounter_type = #{hiv_clinic_registration_id}
|
|
924
|
+
AND hiv_registration.voided = 0 #{site_manager(operator: 'AND', column: 'hiv_registration.site_id',
|
|
925
|
+
location: @location)}
|
|
926
|
+
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(
|
|
927
|
+
date: end_date, value: 1, interval: 'DAY', operator: '+'
|
|
928
|
+
)} #{site_manager(operator: 'AND', column: 'site_id',
|
|
929
|
+
location: @location)}) AS new_patient ON e.patient_id = new_patient.person_id
|
|
930
|
+
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(
|
|
931
|
+
date: end_date, value: 1, interval: 'DAY', operator: '+'
|
|
932
|
+
)} #{site_manager(operator: 'AND', column: 'site_id',
|
|
933
|
+
location: @location)}) AS refill ON e.patient_id = refill.person_id
|
|
934
|
+
LEFT JOIN (SELECT * FROM obs WHERE concept_id = #{type_of_patient_concept} AND voided = 0 AND value_coded = #{external_concept} AND DATE(obs_datetime) < #{interval_manager(
|
|
935
|
+
date: end_date, value: 1, interval: 'DAY', operator: '+'
|
|
936
|
+
)} #{site_manager(operator: 'AND', column: 'site_id',
|
|
937
|
+
location: @location)}) AS external ON e.patient_id = external.person_id
|
|
938
|
+
WHERE (refill.value_coded IS NOT NULL OR external.value_coded IS NOT NULL)
|
|
939
|
+
AND NOT (hiv_registration.encounter_id IS NOT NULL OR new_patient.value_coded IS NOT NULL)
|
|
940
|
+
GROUP BY e.patient_id
|
|
941
|
+
ORDER BY MAX(hiv_registration.encounter_datetime) DESC, MAX(refill.obs_datetime) DESC, MAX(external.obs_datetime) DESC;").each do |record|
|
|
942
|
+
to_remove << record['patient_id'].to_i
|
|
943
|
+
end
|
|
944
|
+
to_remove.join(',')
|
|
945
|
+
end
|
|
946
|
+
# rubocop:enable Metrics/MethodLength
|
|
947
|
+
# rubocop:enable Metrics/AbcSize
|
|
948
|
+
|
|
949
|
+
def create_tmp_patient_table
|
|
950
|
+
ActiveRecord::Base.connection.execute('DROP TABLE IF EXISTS temp_earliest_start_date')
|
|
951
|
+
exe_tmp_patient_table(adapter: @adapter)
|
|
952
|
+
|
|
953
|
+
ActiveRecord::Base.connection.execute(
|
|
954
|
+
'CREATE INDEX patient_id_index ON temp_earliest_start_date (patient_id)'
|
|
955
|
+
)
|
|
956
|
+
ActiveRecord::Base.connection.execute(
|
|
957
|
+
'CREATE INDEX site_id_tesd ON temp_earliest_start_date (site_id)'
|
|
958
|
+
)
|
|
959
|
+
ActiveRecord::Base.connection.execute(
|
|
960
|
+
'CREATE INDEX patient_id__site_id_index ON temp_earliest_start_date (patient_id, site_id)'
|
|
961
|
+
)
|
|
962
|
+
ActiveRecord::Base.connection.execute(
|
|
963
|
+
'CREATE INDEX date_enrolled_index ON temp_earliest_start_date (date_enrolled)'
|
|
964
|
+
)
|
|
965
|
+
ActiveRecord::Base.connection.execute(
|
|
966
|
+
'CREATE INDEX patient_id__date_enrolled_index ON temp_earliest_start_date (patient_id, date_enrolled)'
|
|
967
|
+
)
|
|
968
|
+
ActiveRecord::Base.connection.execute(
|
|
969
|
+
'CREATE INDEX earliest_start_date_index ON temp_earliest_start_date (earliest_start_date)'
|
|
970
|
+
)
|
|
971
|
+
ActiveRecord::Base.connection.execute(
|
|
972
|
+
'CREATE INDEX earliest_start_date__date_enrolled_index ON temp_earliest_start_date (patient_id, earliest_start_date, date_enrolled, gender)'
|
|
973
|
+
)
|
|
974
|
+
ActiveRecord::Base.connection.execute(
|
|
975
|
+
'CREATE INDEX idx_reason_for_art ON temp_earliest_start_date (reason_for_starting_art)'
|
|
976
|
+
)
|
|
977
|
+
ActiveRecord::Base.connection.execute(
|
|
978
|
+
'CREATE INDEX birthdate_idx ON temp_earliest_start_date (birthdate)'
|
|
979
|
+
)
|
|
980
|
+
end
|
|
981
|
+
|
|
982
|
+
def update_cum_outcome(end_date)
|
|
983
|
+
MalawiHivProgramReports::Cohort::Outcomes.new(end_date:, definition: @outcomes_definition, location: @location)
|
|
984
|
+
.update_cummulative_outcomes
|
|
985
|
+
end
|
|
986
|
+
|
|
987
|
+
def update_tb_status(end_date)
|
|
988
|
+
ActiveRecord::Base.connection.execute(
|
|
989
|
+
'DROP TABLE IF EXISTS temp_patient_tb_status'
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
993
|
+
CREATE TABLE temp_patient_tb_status (
|
|
994
|
+
patient_id INT PRIMARY KEY,
|
|
995
|
+
site_id INT DEFAULT #{@location},
|
|
996
|
+
tb_status INT
|
|
997
|
+
)
|
|
998
|
+
SQL
|
|
999
|
+
|
|
1000
|
+
ActiveRecord::Base.connection.execute('CREATE INDEX tb_status_index ON temp_patient_tb_status (tb_status)')
|
|
1001
|
+
ActiveRecord::Base.connection.execute('CREATE INDEX site_id_tpts ON temp_patient_tb_status (site_id)')
|
|
1002
|
+
ActiveRecord::Base.connection.execute('CREATE INDEX patient_id_site_id_index ON temp_patient_tb_status (patient_id, site_id)')
|
|
1003
|
+
ActiveRecord::Base.connection.execute('CREATE INDEX patient_id_tb_status_index ON temp_patient_tb_status (patient_id, tb_status)')
|
|
1004
|
+
|
|
1005
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
1006
|
+
INSERT INTO temp_patient_tb_status (patient_id, tb_status)
|
|
1007
|
+
SELECT e.patient_id, obs.value_coded
|
|
1008
|
+
FROM temp_earliest_start_date e
|
|
1009
|
+
INNER JOIN temp_patient_outcomes o ON o.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
|
1010
|
+
RIGHT JOIN obs ON obs.person_id = o.patient_id #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
1011
|
+
WHERE e.date_enrolled <= '#{end_date}' AND obs.obs_datetime <= '#{end_date} 23:59:59'
|
|
1012
|
+
AND LOWER(cum_outcome) = LOWER('On antiretrovirals') AND obs.voided = 0
|
|
1013
|
+
AND obs.concept_id = 7459 #{site_manager(operator: 'AND', column: 'e.site_id', location: @location)}
|
|
1014
|
+
AND obs.obs_datetime = (
|
|
1015
|
+
SELECT MAX(t.obs_datetime) FROM obs t WHERE t.concept_id = 7459 AND t.voided = 0
|
|
1016
|
+
AND t.person_id = e.patient_id AND t.obs_datetime <= '#{end_date} 23:59:59' #{site_manager(operator: 'AND', column: 't.site_id', location: @location)}
|
|
1017
|
+
) GROUP BY e.patient_id #{@adapter == 'mysql2' ? '' : ', obs.value_coded'}
|
|
1018
|
+
SQL
|
|
1019
|
+
end
|
|
1020
|
+
|
|
1021
|
+
def update_patient_side_effects(end_date)
|
|
1022
|
+
MalawiHivProgramReports::Cohort::SideEffects.new.update_side_effects(end_date, @location)
|
|
1023
|
+
end
|
|
1024
|
+
|
|
1025
|
+
private
|
|
1026
|
+
|
|
1027
|
+
def total_patients_with_screened_bp(total_alive_and_on_art, _start_date, end_date)
|
|
1028
|
+
return 0 if total_alive_and_on_art.blank? || total_alive_and_on_art.empty?
|
|
1029
|
+
|
|
1030
|
+
bp_concepts = ::ConceptName.where(name: ['Systolic blood pressure', 'Diastolic blood pressure'])
|
|
1031
|
+
.select(:concept_id)
|
|
1032
|
+
|
|
1033
|
+
results = ActiveRecord::Base.connection.select_all <<~SQL
|
|
1034
|
+
SELECT o.person_id
|
|
1035
|
+
FROM obs o
|
|
1036
|
+
INNER JOIN (
|
|
1037
|
+
SELECT person_id, MAX(obs.obs_datetime) AS obs_datetime
|
|
1038
|
+
FROM obs
|
|
1039
|
+
WHERE voided = 0 AND concept_id IN (#{bp_concepts.to_sql})
|
|
1040
|
+
AND (value_text IS NOT NULL OR value_numeric IS NOT NULL)
|
|
1041
|
+
AND obs_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')} AND obs_datetime >= #{interval_manager(date: end_date, value: 12, interval: 'MONTH', operator: '-')}
|
|
1042
|
+
AND #{in_manager(column: 'person_id', values: total_alive_and_on_art)} #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
1043
|
+
GROUP BY person_id
|
|
1044
|
+
) AS max_obs
|
|
1045
|
+
ON max_obs.person_id = o.person_id
|
|
1046
|
+
AND max_obs.obs_datetime = o.obs_datetime
|
|
1047
|
+
WHERE o.voided = 0 #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
|
1048
|
+
AND o.concept_id IN (#{bp_concepts.to_sql})
|
|
1049
|
+
AND (o.value_text IS NOT NULL OR o.value_numeric IS NOT NULL)
|
|
1050
|
+
GROUP BY o.person_id
|
|
1051
|
+
SQL
|
|
1052
|
+
|
|
1053
|
+
((results.count.to_f / total_alive_and_on_art.count) * 100).to_i
|
|
1054
|
+
end
|
|
1055
|
+
|
|
1056
|
+
def total_patients_alive_and_on_art_above_30_years(total_alive_and_on_art, end_date)
|
|
1057
|
+
return nil if total_alive_and_on_art.blank?
|
|
1058
|
+
return nil if total_alive_and_on_art.empty?
|
|
1059
|
+
|
|
1060
|
+
results = ActiveRecord::Base.connection.select_all <<~SQL
|
|
1061
|
+
SELECT tesd.patient_id, #{@adapter == 'mysql2' ? "TIMESTAMPDIFF(YEAR, tesd.birthdate, DATE('#{end_date}')) AS age" : "(EXTRACT(YEAR FROM AGE(DATE('#{end_date}'), tesd.birthdate))) AS age"}
|
|
1062
|
+
FROM temp_earliest_start_date tesd
|
|
1063
|
+
WHERE #{in_manager(column: 'tesd.patient_id', values: total_alive_and_on_art.map { |r| r['patient_id'].to_i }.join(','))}
|
|
1064
|
+
GROUP BY tesd.patient_id
|
|
1065
|
+
HAVING #{@adapter == 'mysql2' ? 'age' : "(EXTRACT(YEAR FROM AGE(DATE('#{end_date}'), tesd.birthdate)))"} >= 30
|
|
1066
|
+
SQL
|
|
1067
|
+
|
|
1068
|
+
# map the results to patient ids
|
|
1069
|
+
results&.map { |r| r['patient_id'].to_i }
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
def total_patients_on_family_planning(patients_list, start_date, end_date)
|
|
1073
|
+
patient_ids = []
|
|
1074
|
+
patient_list = []
|
|
1075
|
+
|
|
1076
|
+
(patients_list || []).each do |row|
|
|
1077
|
+
patient_ids << row['patient_id'].to_i
|
|
1078
|
+
end
|
|
1079
|
+
|
|
1080
|
+
return [] if patient_ids.blank?
|
|
1081
|
+
|
|
1082
|
+
all_women = ActiveRecord::Base.connection.select_all(
|
|
1083
|
+
"SELECT * FROM temp_earliest_start_date
|
|
1084
|
+
WHERE gender IN ('F','Female') AND patient_id IN (#{patient_ids.join(',')})
|
|
1085
|
+
AND date_enrolled BETWEEN '#{start_date.to_date}' AND '#{end_date.to_date}' #{site_manager(operator: 'AND',
|
|
1086
|
+
column: 'site_id', location: @location)}
|
|
1087
|
+
GROUP BY patient_id"
|
|
1088
|
+
)
|
|
1089
|
+
|
|
1090
|
+
(all_women || []).each do |patient|
|
|
1091
|
+
patient_list << patient['patient_id'].to_i
|
|
1092
|
+
end
|
|
1093
|
+
|
|
1094
|
+
return 0 if patient_list.blank?
|
|
1095
|
+
|
|
1096
|
+
hiv_clinic_consultation_encounter_type_id = encounter_type('HIV CLINIC CONSULTATION').encounter_type_id
|
|
1097
|
+
method_of_family_planning_concept_id = concept('Method of family planning').concept_id
|
|
1098
|
+
family_planning_action_to_take_concept_id = concept('Family planning, action to take').concept_id
|
|
1099
|
+
none_concept_id = [concept('None').concept_id, concept('No').concept_id]
|
|
1100
|
+
|
|
1101
|
+
results = ActiveRecord::Base.connection.select_all(
|
|
1102
|
+
"SELECT o.person_id
|
|
1103
|
+
FROM obs o
|
|
1104
|
+
INNER JOIN encounter e on e.encounter_id = o.encounter_id AND e.encounter_type = #{hiv_clinic_consultation_encounter_type_id} #{site_manager(
|
|
1105
|
+
operator: 'AND', column: 'e.site_id', location: @location
|
|
1106
|
+
)}
|
|
1107
|
+
WHERE o.voided = 0 AND e.voided = 0 #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
|
1108
|
+
AND #{in_manager(column: 'o.concept_id',
|
|
1109
|
+
values: [family_planning_action_to_take_concept_id,
|
|
1110
|
+
method_of_family_planning_concept_id])}
|
|
1111
|
+
AND #{in_manager(column: 'o.value_coded', values: none_concept_id.join(','), negation: true)}
|
|
1112
|
+
AND #{in_manager(column: 'o.person_id', values: patient_list.join(','))}
|
|
1113
|
+
AND o.obs_datetime BETWEEN '#{start_date.to_date.strftime('%Y-%m-%d 00:00:00')}'
|
|
1114
|
+
AND '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
|
|
1115
|
+
AND DATE(o.obs_datetime) = (SELECT max(date(obs.obs_datetime)) FROM obs obs
|
|
1116
|
+
WHERE obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
1117
|
+
AND #{in_manager(column: 'obs.concept_id',
|
|
1118
|
+
values: [family_planning_action_to_take_concept_id,
|
|
1119
|
+
method_of_family_planning_concept_id])}
|
|
1120
|
+
AND obs.obs_datetime BETWEEN '#{start_date.to_date.strftime('%Y-%m-%d 00:00:00')}' AND '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
|
|
1121
|
+
AND obs.person_id = o.person_id) #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
|
1122
|
+
GROUP BY o.person_id"
|
|
1123
|
+
)
|
|
1124
|
+
|
|
1125
|
+
begin
|
|
1126
|
+
((results.count.to_f / patient_list.count) * 100).to_i
|
|
1127
|
+
rescue StandardError
|
|
1128
|
+
0
|
|
1129
|
+
end
|
|
1130
|
+
end
|
|
1131
|
+
|
|
1132
|
+
def total_patients_on_arvs_and_ipt(patients_list, start_date, end_date)
|
|
1133
|
+
isoniazid_concept_id = concept('Isoniazid').concept_id
|
|
1134
|
+
pyridoxine_concept_id = concept('Pyridoxine').concept_id
|
|
1135
|
+
|
|
1136
|
+
patient_ids = []
|
|
1137
|
+
(patients_list || []).each do |row|
|
|
1138
|
+
patient_ids << row['patient_id'].to_i
|
|
1139
|
+
end
|
|
1140
|
+
|
|
1141
|
+
return [] if patient_ids.blank?
|
|
1142
|
+
|
|
1143
|
+
results = ActiveRecord::Base.connection.select_all(
|
|
1144
|
+
"SELECT ods.patient_id
|
|
1145
|
+
FROM orders ods
|
|
1146
|
+
INNER JOIN drug_order dos ON ods.order_id = dos.order_id AND ods.voided = 0 #{site_manager(operator: 'AND',
|
|
1147
|
+
column: 'dos.site_id', location: @location)}
|
|
1148
|
+
WHERE #{in_manager(column: 'ods.concept_id', values: [isoniazid_concept_id, pyridoxine_concept_id])}
|
|
1149
|
+
AND dos.quantity IS NOT NULL #{site_manager(operator: 'AND', column: 'ods.site_id', location: @location)}
|
|
1150
|
+
AND #{in_manager(column: 'ods.patient_id', values: patient_ids)}
|
|
1151
|
+
AND ods.start_date BETWEEN '#{start_date.to_date.strftime('%Y-%m-%d 00:00:00')}'
|
|
1152
|
+
AND '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
|
|
1153
|
+
AND DATE(ods.start_date) = (SELECT MAX(DATE(o.start_date)) FROM orders o
|
|
1154
|
+
INNER JOIN drug_order d ON o.order_id = d.order_id AND o.voided = 0 #{site_manager(
|
|
1155
|
+
operator: 'AND', column: 'd.site_id', location: @location
|
|
1156
|
+
)}
|
|
1157
|
+
WHERE #{in_manager(column: 'o.concept_id',
|
|
1158
|
+
values: [
|
|
1159
|
+
isoniazid_concept_id, pyridoxine_concept_id
|
|
1160
|
+
])}
|
|
1161
|
+
AND o.patient_id = ods.patient_id #{site_manager(operator: 'AND',
|
|
1162
|
+
column: 'o.site_id', location: @location)}
|
|
1163
|
+
AND d.quantity IS NOT NULL
|
|
1164
|
+
AND o.start_date BETWEEN '#{start_date.to_date.strftime('%Y-%m-%d 00:00:00')}'
|
|
1165
|
+
AND '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}')
|
|
1166
|
+
GROUP BY ods.patient_id"
|
|
1167
|
+
)
|
|
1168
|
+
|
|
1169
|
+
((results.count.to_f / patient_ids.count) * 100).to_i
|
|
1170
|
+
end
|
|
1171
|
+
|
|
1172
|
+
def total_patients_on_arvs_and_cpt(patients_list, start_date, end_date)
|
|
1173
|
+
cpt_concept_id = concept('Cotrimoxazole').concept_id
|
|
1174
|
+
|
|
1175
|
+
patient_ids = []
|
|
1176
|
+
(patients_list || []).each do |row|
|
|
1177
|
+
patient_ids << row['patient_id'].to_i
|
|
1178
|
+
end
|
|
1179
|
+
|
|
1180
|
+
return [] if patient_ids.blank?
|
|
1181
|
+
|
|
1182
|
+
results = ActiveRecord::Base.connection.select_all(
|
|
1183
|
+
"SELECT ods.patient_id
|
|
1184
|
+
FROM orders ods
|
|
1185
|
+
INNER JOIN drug_order dos ON ods.order_id = dos.order_id AND ods.voided = 0 #{site_manager(operator: 'AND',
|
|
1186
|
+
column: 'dos.site_id', location: @location)}
|
|
1187
|
+
WHERE ods.concept_id = #{cpt_concept_id}
|
|
1188
|
+
AND dos.quantity > 0 #{site_manager(operator: 'AND', column: 'ods.site_id', location: @location)}
|
|
1189
|
+
AND ods.patient_id in (#{patient_ids.join(',')})
|
|
1190
|
+
AND ods.start_date BETWEEN '#{start_date.to_date.strftime('%Y-%m-%d 00:00:00')}'
|
|
1191
|
+
AND '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
|
|
1192
|
+
AND DATE(ods.start_date) = (SELECT MAX(DATE(o.start_date)) FROM orders o
|
|
1193
|
+
INNER JOIN drug_order d ON o.order_id = d.order_id AND o.voided = 0 #{site_manager(
|
|
1194
|
+
operator: 'AND', column: 'd.site_id', location: @location
|
|
1195
|
+
)}
|
|
1196
|
+
WHERE o.concept_id = #{cpt_concept_id} #{site_manager(operator: 'AND',
|
|
1197
|
+
column: 'o.site_id', location: @location)}
|
|
1198
|
+
AND d.quantity > 0
|
|
1199
|
+
AND o.patient_id = ods.patient_id
|
|
1200
|
+
AND o.start_date BETWEEN '#{start_date.to_date.strftime('%Y-%m-%d 00:00:00')}'
|
|
1201
|
+
AND '#{end_date.to_date.strftime('%Y-%m-%d 23:59:59')}')
|
|
1202
|
+
|
|
1203
|
+
GROUP BY ods.patient_id"
|
|
1204
|
+
)
|
|
1205
|
+
|
|
1206
|
+
((results.count.to_f / patient_ids.count) * 100).to_i
|
|
1207
|
+
end
|
|
1208
|
+
|
|
1209
|
+
def total_breastfeeding_women(_patients_list, total_pregnant_women, _start_date, end_date)
|
|
1210
|
+
total_pregnant_women = if total_pregnant_women.empty?
|
|
1211
|
+
[0]
|
|
1212
|
+
else
|
|
1213
|
+
total_pregnant_women.map { |woman| woman['person_id'].to_i }
|
|
1214
|
+
end
|
|
1215
|
+
|
|
1216
|
+
encounter_types = ::EncounterType.where('LOWER(name) IN (?)',
|
|
1217
|
+
['HIV CLINIC CONSULTATION'.downcase, 'HIV STAGING'.downcase])
|
|
1218
|
+
.select(:encounter_type_id)
|
|
1219
|
+
|
|
1220
|
+
breastfeeding_concepts = ::ConceptName.where('LOWER(name) IN (?)',
|
|
1221
|
+
['Breast feeding?'.downcase, 'Breast feeding'.downcase,
|
|
1222
|
+
'Breastfeeding'.downcase])
|
|
1223
|
+
.select(:concept_id)
|
|
1224
|
+
|
|
1225
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
|
1226
|
+
SELECT obs.person_id, obs.value_coded
|
|
1227
|
+
FROM obs
|
|
1228
|
+
INNER JOIN encounter enc
|
|
1229
|
+
ON enc.encounter_id = obs.encounter_id
|
|
1230
|
+
AND enc.voided = 0 #{site_manager(operator: 'AND', column: 'enc.site_id', location: @location)}
|
|
1231
|
+
AND enc.encounter_type IN (#{encounter_types.to_sql})
|
|
1232
|
+
INNER JOIN temp_earliest_start_date e
|
|
1233
|
+
ON e.patient_id = enc.patient_id #{site_manager(operator: 'AND', column: 'e.site_id', location: @location)}
|
|
1234
|
+
AND LEFT(e.gender, 1) = 'F'
|
|
1235
|
+
INNER JOIN temp_patient_outcomes
|
|
1236
|
+
ON temp_patient_outcomes.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 'temp_patient_outcomes.site_id', location: @location)}
|
|
1237
|
+
AND temp_patient_outcomes.cum_outcome = 'On antiretrovirals'
|
|
1238
|
+
INNER JOIN (
|
|
1239
|
+
SELECT person_id, MAX(obs_datetime) AS obs_datetime
|
|
1240
|
+
FROM obs
|
|
1241
|
+
INNER JOIN encounter
|
|
1242
|
+
ON encounter.encounter_id = obs.encounter_id
|
|
1243
|
+
AND encounter.encounter_type IN (#{encounter_types.to_sql})
|
|
1244
|
+
AND encounter.voided = 0 #{site_manager(operator: 'AND', column: 'encounter.site_id', location: @location)}
|
|
1245
|
+
WHERE person_id IN (SELECT patient_id FROM temp_patient_outcomes WHERE cum_outcome = 'On antiretrovirals')
|
|
1246
|
+
AND concept_id IN (#{breastfeeding_concepts.to_sql})
|
|
1247
|
+
AND obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
1248
|
+
AND obs_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')}
|
|
1249
|
+
GROUP BY person_id
|
|
1250
|
+
) AS max_obs
|
|
1251
|
+
ON max_obs.person_id = obs.person_id
|
|
1252
|
+
AND max_obs.obs_datetime = obs.obs_datetime
|
|
1253
|
+
WHERE obs.person_id = e.patient_id
|
|
1254
|
+
AND #{in_manager(column: 'obs.person_id', values: total_pregnant_women.join(','), negation: true)}
|
|
1255
|
+
AND obs.obs_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')}
|
|
1256
|
+
AND obs.concept_id IN (#{breastfeeding_concepts.to_sql})
|
|
1257
|
+
AND obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
1258
|
+
GROUP BY obs.person_id #{@adapter == 'mysql2' ? '' : ', obs.value_coded'}
|
|
1259
|
+
HAVING value_coded = 1065
|
|
1260
|
+
ORDER BY MAX(obs.obs_datetime) DESC;
|
|
1261
|
+
SQL
|
|
1262
|
+
end
|
|
1263
|
+
|
|
1264
|
+
def total_pregnant_women(_patients_list, _start_date, end_date)
|
|
1265
|
+
encounter_types = ::EncounterType.where('LOWER(name) IN (?)',
|
|
1266
|
+
['HIV CLINIC CONSULTATION'.downcase, 'HIV STAGING'.downcase])
|
|
1267
|
+
.select(:encounter_type_id)
|
|
1268
|
+
|
|
1269
|
+
pregnant_concepts = ::ConceptName.where('LOWER(name) IN (?)',
|
|
1270
|
+
['Is patient pregnant?'.downcase, 'patient pregnant'.downcase])
|
|
1271
|
+
.select(:concept_id)
|
|
1272
|
+
|
|
1273
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
|
1274
|
+
SELECT obs.person_id, obs.value_coded
|
|
1275
|
+
FROM obs obs
|
|
1276
|
+
INNER JOIN encounter enc
|
|
1277
|
+
ON enc.encounter_id = obs.encounter_id
|
|
1278
|
+
AND enc.voided = 0 #{site_manager(operator: 'AND', column: 'enc.site_id', location: @location)}
|
|
1279
|
+
AND enc.encounter_type IN (#{encounter_types.to_sql})
|
|
1280
|
+
INNER JOIN temp_earliest_start_date e
|
|
1281
|
+
ON e.patient_id = enc.patient_id #{site_manager(operator: 'AND', column: 'e.site_id', location: @location)}
|
|
1282
|
+
AND LEFT(e.gender, 1) = 'F'
|
|
1283
|
+
INNER JOIN temp_patient_outcomes
|
|
1284
|
+
ON temp_patient_outcomes.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 'temp_patient_outcomes.site_id', location: @location)}
|
|
1285
|
+
AND temp_patient_outcomes.cum_outcome = 'On antiretrovirals'
|
|
1286
|
+
INNER JOIN (
|
|
1287
|
+
SELECT person_id, MAX(obs_datetime) AS obs_datetime
|
|
1288
|
+
FROM obs
|
|
1289
|
+
INNER JOIN encounter
|
|
1290
|
+
ON encounter.encounter_id = obs.encounter_id #{site_manager(operator: 'AND', column: 'encounter.site_id', location: @location)}
|
|
1291
|
+
AND encounter.encounter_type IN (#{encounter_types.to_sql})
|
|
1292
|
+
AND encounter.voided = 0
|
|
1293
|
+
WHERE concept_id IN (#{pregnant_concepts.to_sql})
|
|
1294
|
+
AND obs_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')}
|
|
1295
|
+
AND obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
1296
|
+
GROUP BY person_id
|
|
1297
|
+
) AS max_obs
|
|
1298
|
+
ON max_obs.person_id = obs.person_id
|
|
1299
|
+
AND max_obs.obs_datetime = obs.obs_datetime
|
|
1300
|
+
WHERE obs.concept_id IN (#{pregnant_concepts.to_sql})
|
|
1301
|
+
AND obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
1302
|
+
GROUP BY obs.person_id #{@adapter == 'mysql2' ? '' : ', obs.value_coded'}
|
|
1303
|
+
HAVING obs.value_coded = 1065
|
|
1304
|
+
ORDER BY MAX(obs.obs_datetime) DESC;
|
|
1305
|
+
SQL
|
|
1306
|
+
end
|
|
1307
|
+
|
|
1308
|
+
def total_other_patients(patient_list, all_breastfeeding_women, all_pregnant_women)
|
|
1309
|
+
patient_ids = []
|
|
1310
|
+
all_pregnant_women_ids = []
|
|
1311
|
+
all_breastfeeding_women_ids = []
|
|
1312
|
+
|
|
1313
|
+
(patient_list || []).each do |row|
|
|
1314
|
+
patient_ids << row['patient_id'].to_i
|
|
1315
|
+
end
|
|
1316
|
+
|
|
1317
|
+
(all_pregnant_women || []).each do |row|
|
|
1318
|
+
all_pregnant_women_ids << row['person_id'].to_i
|
|
1319
|
+
end
|
|
1320
|
+
|
|
1321
|
+
(all_breastfeeding_women || []).each do |row|
|
|
1322
|
+
all_breastfeeding_women_ids << row['person_id'].to_i
|
|
1323
|
+
end
|
|
1324
|
+
|
|
1325
|
+
(patient_ids - (all_breastfeeding_women_ids + all_pregnant_women_ids))
|
|
1326
|
+
end
|
|
1327
|
+
|
|
1328
|
+
MIN_ART_ADHERENCE_THRESHOLD = 95.0 # Those below are not adherent
|
|
1329
|
+
MAX_ART_ADHERENCE_THRESHOLD = 105.0 # Thoseabove are not adherent
|
|
1330
|
+
|
|
1331
|
+
# Groups patients list into three groups based on the adherence rates
|
|
1332
|
+
#
|
|
1333
|
+
# Returns: A list of 3 lists as follows:
|
|
1334
|
+
# [
|
|
1335
|
+
# [adherent patients],
|
|
1336
|
+
# [inadherent patients],
|
|
1337
|
+
# [patients whose adherence rate is unknown]
|
|
1338
|
+
# ]
|
|
1339
|
+
def latest_art_adherence(patients_alive_and_on_art, _start_date, end_date)
|
|
1340
|
+
patients_alive_and_on_art = Set.new(patients_alive_and_on_art.map { |patient| patient['patient_id'] })
|
|
1341
|
+
end_date = ActiveRecord::Base.connection.quote(end_date)
|
|
1342
|
+
|
|
1343
|
+
not_adherent = ActiveRecord::Base.connection.select_all <<~SQL
|
|
1344
|
+
SELECT adherence.person_id
|
|
1345
|
+
FROM obs AS adherence
|
|
1346
|
+
INNER JOIN (
|
|
1347
|
+
SELECT obs.person_id, DATE(MAX(obs.obs_datetime)) AS visit_date
|
|
1348
|
+
FROM obs
|
|
1349
|
+
INNER JOIN orders
|
|
1350
|
+
ON orders.order_id = obs.order_id #{site_manager(operator: 'AND', column: 'orders.site_id', location: @location)}
|
|
1351
|
+
AND orders.concept_id IN (#{arv_drug_concepts.to_sql})
|
|
1352
|
+
AND orders.order_type_id = #{drug_order_type.order_type_id}
|
|
1353
|
+
AND orders.voided = 0
|
|
1354
|
+
INNER JOIN temp_patient_outcomes
|
|
1355
|
+
ON temp_patient_outcomes.patient_id = obs.person_id #{site_manager(operator: 'AND', column: 'temp_patient_outcomes.site_id', location: @location)}
|
|
1356
|
+
AND LOWER(temp_patient_outcomes.cum_outcome) = LOWER('On antiretrovirals')
|
|
1357
|
+
WHERE obs.concept_id = #{drug_order_adherence_concept.concept_id}
|
|
1358
|
+
AND obs.obs_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')}
|
|
1359
|
+
AND (obs.value_numeric IS NOT NULL OR obs.value_text IS NOT NULL)
|
|
1360
|
+
AND obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
1361
|
+
GROUP BY obs.person_id
|
|
1362
|
+
) AS max_adherence
|
|
1363
|
+
ON max_adherence.person_id = adherence.person_id
|
|
1364
|
+
AND adherence.obs_datetime >= max_adherence.visit_date #{site_manager(operator: 'AND', column: 'adherence.site_id', location: @location)}
|
|
1365
|
+
AND adherence.obs_datetime < #{interval_manager(date: 'max_adherence.visit_date', value: 1, interval: 'DAY', operator: '+')}
|
|
1366
|
+
INNER JOIN orders
|
|
1367
|
+
ON orders.order_id = adherence.order_id
|
|
1368
|
+
AND orders.order_type_id = #{drug_order_type.order_type_id}
|
|
1369
|
+
AND orders.concept_id IN (#{arv_drug_concepts.to_sql})
|
|
1370
|
+
AND orders.voided = 0 #{site_manager(operator: 'AND', column: 'orders.site_id', location: @location)}
|
|
1371
|
+
WHERE adherence.concept_id = #{drug_order_adherence_concept.concept_id}
|
|
1372
|
+
AND (
|
|
1373
|
+
(adherence.value_numeric < #{MIN_ART_ADHERENCE_THRESHOLD} OR adherence.value_numeric > #{MAX_ART_ADHERENCE_THRESHOLD})
|
|
1374
|
+
OR
|
|
1375
|
+
(#{@adapter == 'mysql2' ? 'CAST(adherence.value_text AS SIGNED INTEGER)' : 'CAST(adherence.value_text AS INTEGER)'} < #{MIN_ART_ADHERENCE_THRESHOLD}
|
|
1376
|
+
OR #{@adapter == 'mysql2' ? 'CAST(adherence.value_text AS SIGNED INTEGER)' : 'CAST(adherence.value_text AS INTEGER)'} > #{MAX_ART_ADHERENCE_THRESHOLD})
|
|
1377
|
+
)
|
|
1378
|
+
AND adherence.voided = 0 #{site_manager(operator: 'AND', column: 'adherence.site_id', location: @location)}
|
|
1379
|
+
GROUP BY adherence.person_id
|
|
1380
|
+
SQL
|
|
1381
|
+
|
|
1382
|
+
not_adherent = not_adherent.empty? ? [] : not_adherent.map { |row| row['person_id'] }
|
|
1383
|
+
|
|
1384
|
+
adherent = ActiveRecord::Base.connection.select_all <<~SQL
|
|
1385
|
+
SELECT adherence.person_id
|
|
1386
|
+
FROM obs AS adherence
|
|
1387
|
+
INNER JOIN (
|
|
1388
|
+
SELECT obs.person_id, DATE(MAX(obs.obs_datetime)) AS visit_date
|
|
1389
|
+
FROM obs
|
|
1390
|
+
INNER JOIN orders
|
|
1391
|
+
ON orders.order_id = obs.order_id
|
|
1392
|
+
AND orders.concept_id IN (#{arv_drug_concepts.to_sql})
|
|
1393
|
+
AND orders.order_type_id = #{drug_order_type.order_type_id}
|
|
1394
|
+
AND orders.voided = 0 #{site_manager(operator: 'AND', column: 'orders.site_id', location: @location)}
|
|
1395
|
+
INNER JOIN temp_patient_outcomes
|
|
1396
|
+
ON temp_patient_outcomes.patient_id = obs.person_id #{site_manager(operator: 'AND', column: 'temp_patient_outcomes.site_id', location: @location)}
|
|
1397
|
+
AND #{in_manager(column: 'temp_patient_outcomes.patient_id', values: not_adherent.blank? ? [0] : not_adherent.join(','), negation: true)}
|
|
1398
|
+
AND LOWER(temp_patient_outcomes.cum_outcome) = LOWER('On antiretrovirals')
|
|
1399
|
+
WHERE obs.concept_id = #{drug_order_adherence_concept.concept_id}
|
|
1400
|
+
AND obs.obs_datetime < #{interval_manager(date: end_date, value: 1, interval: 'DAY', operator: '+')}
|
|
1401
|
+
AND (obs.value_numeric IS NOT NULL OR obs.value_text IS NOT NULL)
|
|
1402
|
+
AND obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
1403
|
+
GROUP BY obs.person_id
|
|
1404
|
+
) AS max_adherence
|
|
1405
|
+
ON max_adherence.person_id = adherence.person_id
|
|
1406
|
+
AND adherence.obs_datetime >= max_adherence.visit_date #{site_manager(operator: 'AND', column: 'adherence.site_id', location: @location)}
|
|
1407
|
+
AND adherence.obs_datetime < #{interval_manager(date: 'max_adherence.visit_date', value: 1, interval: 'DAY', operator: '+')}
|
|
1408
|
+
INNER JOIN orders
|
|
1409
|
+
ON orders.order_id = adherence.order_id
|
|
1410
|
+
AND orders.order_type_id = #{drug_order_type.order_type_id}
|
|
1411
|
+
AND orders.concept_id IN (#{arv_drug_concepts.to_sql})
|
|
1412
|
+
AND orders.voided = 0 #{site_manager(operator: 'AND', column: 'orders.site_id', location: @location)}
|
|
1413
|
+
WHERE adherence.concept_id = #{drug_order_adherence_concept.concept_id}
|
|
1414
|
+
AND ((adherence.value_numeric >= #{MIN_ART_ADHERENCE_THRESHOLD}
|
|
1415
|
+
OR adherence.value_numeric <= #{MAX_ART_ADHERENCE_THRESHOLD})
|
|
1416
|
+
OR (#{@adapter == 'mysql2' ? 'CAST(adherence.value_text AS SIGNED INTEGER)' : 'CAST(adherence.value_text AS INTEGER)'} >= #{MIN_ART_ADHERENCE_THRESHOLD}
|
|
1417
|
+
OR #{@adapter == 'mysql2' ? 'CAST(adherence.value_text AS SIGNED INTEGER)' : 'CAST(adherence.value_text AS INTEGER)'} <= #{MAX_ART_ADHERENCE_THRESHOLD}))
|
|
1418
|
+
AND adherence.voided = 0 #{site_manager(operator: 'AND', column: 'adherence.site_id', location: @location)}
|
|
1419
|
+
GROUP BY adherence.person_id
|
|
1420
|
+
SQL
|
|
1421
|
+
|
|
1422
|
+
adherent = adherent.map { |row| row['person_id'] }
|
|
1423
|
+
unknown_adherence = patients_alive_and_on_art.to_a - adherent - not_adherent
|
|
1424
|
+
Rails.logger.info "Adherent: #{adherent.uniq.count}, Not Adherent: #{not_adherent.uniq.count}, Unknown: #{unknown_adherence.uniq.count}"
|
|
1425
|
+
[adherent.uniq, not_adherent.uniq, unknown_adherence]
|
|
1426
|
+
end
|
|
1427
|
+
|
|
1428
|
+
def adherence_encounter
|
|
1429
|
+
@adherence_encounter ||= encounter_type('ART ADHERENCE')
|
|
1430
|
+
end
|
|
1431
|
+
|
|
1432
|
+
def hiv_program
|
|
1433
|
+
@hiv_program ||= program('HIV PROGRAM')
|
|
1434
|
+
end
|
|
1435
|
+
|
|
1436
|
+
def drug_order_adherence_concept
|
|
1437
|
+
@drug_order_adherence_concept ||= concept('Drug order adherence')
|
|
1438
|
+
end
|
|
1439
|
+
|
|
1440
|
+
def drug_order_type
|
|
1441
|
+
@drug_order_type ||= order_type('Drug order')
|
|
1442
|
+
end
|
|
1443
|
+
|
|
1444
|
+
def arv_drug_concepts
|
|
1445
|
+
@arv_drug_concepts ||= ::ConceptSet.where(set: concept('Antiretroviral drugs'))
|
|
1446
|
+
.select(:concept_id)
|
|
1447
|
+
end
|
|
1448
|
+
|
|
1449
|
+
def write_tb_status_indicators(cohort_struct, _patients_alive_and_on_art, _start_date, end_date)
|
|
1450
|
+
cohort_struct.tb_suspected = []
|
|
1451
|
+
cohort_struct.tb_not_suspected = []
|
|
1452
|
+
cohort_struct.tb_confirmed_on_tb_treatment = []
|
|
1453
|
+
cohort_struct.tb_confirmed_currently_not_yet_on_tb_treatment = []
|
|
1454
|
+
cohort_struct.unknown_tb_status = []
|
|
1455
|
+
|
|
1456
|
+
tb_suspected_concept = concept('TB Suspected')
|
|
1457
|
+
tb_not_suspected_concept = concept('TB Not Suspected')
|
|
1458
|
+
tb_confirmed_but_not_on_treatment = concept('Confirmed TB NOT on Treatment')
|
|
1459
|
+
tb_confirmed_and_on_treatment = concept('Confirmed TB on Treatment')
|
|
1460
|
+
|
|
1461
|
+
# patients_alive_and_on_art
|
|
1462
|
+
(all_tb_statuses(end_date) || []).each do |data|
|
|
1463
|
+
tb_status_value = begin
|
|
1464
|
+
data['tb_status'].to_i
|
|
1465
|
+
rescue StandardError
|
|
1466
|
+
nil
|
|
1467
|
+
end
|
|
1468
|
+
|
|
1469
|
+
case tb_status_value
|
|
1470
|
+
when tb_suspected_concept.concept_id
|
|
1471
|
+
cohort_struct.tb_suspected << data['patient_id']
|
|
1472
|
+
when tb_not_suspected_concept.concept_id
|
|
1473
|
+
cohort_struct.tb_not_suspected << data['patient_id']
|
|
1474
|
+
when tb_confirmed_and_on_treatment.concept_id
|
|
1475
|
+
cohort_struct.tb_confirmed_on_tb_treatment << data['patient_id']
|
|
1476
|
+
when tb_confirmed_but_not_on_treatment.concept_id
|
|
1477
|
+
cohort_struct.tb_confirmed_currently_not_yet_on_tb_treatment << data['patient_id']
|
|
1478
|
+
else
|
|
1479
|
+
cohort_struct.unknown_tb_status << data['patient_id']
|
|
1480
|
+
end
|
|
1481
|
+
end
|
|
1482
|
+
end
|
|
1483
|
+
|
|
1484
|
+
def all_tb_statuses(end_date)
|
|
1485
|
+
ActiveRecord::Base.connection.select_all("
|
|
1486
|
+
SELECT e.*, tb_status
|
|
1487
|
+
FROM temp_earliest_start_date e
|
|
1488
|
+
LEFT JOIN temp_patient_tb_status s ON s.patient_id = e.patient_id #{site_manager(operator: 'AND',
|
|
1489
|
+
column: 's.site_id', location: @location)}
|
|
1490
|
+
INNER JOIN temp_patient_outcomes o ON o.patient_id = e.patient_id #{site_manager(operator: 'AND',
|
|
1491
|
+
column: 'o.site_id', location: @location)}
|
|
1492
|
+
WHERE LOWER(o.cum_outcome) = LOWER('On antiretrovirals') #{site_manager(operator: 'AND', column: 'e.site_id',
|
|
1493
|
+
location: @location)}
|
|
1494
|
+
AND DATE(e.date_enrolled) <= '#{end_date.to_date}';
|
|
1495
|
+
")
|
|
1496
|
+
end
|
|
1497
|
+
|
|
1498
|
+
def get_tb_status(tb_status)
|
|
1499
|
+
registered = []
|
|
1500
|
+
(@tb_status || []).each do |status|
|
|
1501
|
+
if tb_status == status[:tb_status]
|
|
1502
|
+
registered << { patient_id: status[:patient_id], tb_status: status[:tb_status] }
|
|
1503
|
+
end
|
|
1504
|
+
end
|
|
1505
|
+
|
|
1506
|
+
registered
|
|
1507
|
+
end
|
|
1508
|
+
|
|
1509
|
+
def patients_side_effects_status(_patients_alive_and_on_art, end_date)
|
|
1510
|
+
with_side_effects = []
|
|
1511
|
+
without_side_effects = []
|
|
1512
|
+
unknowns = []
|
|
1513
|
+
|
|
1514
|
+
records = ActiveRecord::Base.connection.select_all <<~SQL
|
|
1515
|
+
SELECT e.*, s.has_se
|
|
1516
|
+
FROM temp_earliest_start_date e
|
|
1517
|
+
INNER JOIN temp_patient_side_effects s ON s.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 's.site_id', location: @location)}
|
|
1518
|
+
INNER JOIN temp_patient_outcomes o ON o.patient_id = e.patient_id #{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
|
1519
|
+
WHERE LOWER(o.cum_outcome) = LOWER('On antiretrovirals') #{site_manager(operator: 'AND', column: 'e.site_id', location: @location)}
|
|
1520
|
+
AND DATE(e.date_enrolled) <= '#{end_date.to_date}';
|
|
1521
|
+
SQL
|
|
1522
|
+
|
|
1523
|
+
(records || []).each do |data|
|
|
1524
|
+
if data['has_se'] == 'Yes'
|
|
1525
|
+
with_side_effects << data['patient_id']
|
|
1526
|
+
elsif data['has_se'] == 'No'
|
|
1527
|
+
without_side_effects << data['patient_id']
|
|
1528
|
+
else
|
|
1529
|
+
unknowns << data['patient_id']
|
|
1530
|
+
end
|
|
1531
|
+
end
|
|
1532
|
+
|
|
1533
|
+
[with_side_effects, without_side_effects, unknowns]
|
|
1534
|
+
end
|
|
1535
|
+
|
|
1536
|
+
COHORT_REGIMENS = %w[
|
|
1537
|
+
0P 2P 4PP 4PA 9PP 9PA 11PP 11PA 12PP 12PA 14PP 14PA 15PP 15PA 16P 17PP 17PA
|
|
1538
|
+
4A 5A 6A 7A 8A 9A 10A 11A 12A 13A 14A 15A 16A 17A
|
|
1539
|
+
].freeze
|
|
1540
|
+
|
|
1541
|
+
def cal_regimem_category(_patient_list, end_date)
|
|
1542
|
+
MalawiHivProgramReports::Cohort::Regimens.new.patient_regimens(end_date, @location).map do |prescription|
|
|
1543
|
+
regimen = prescription['regimen_category']&.upcase
|
|
1544
|
+
|
|
1545
|
+
regimen = 'unknown_regimen' if regimen == 'Unknown'.upcase || !COHORT_REGIMENS.include?(regimen)
|
|
1546
|
+
|
|
1547
|
+
{
|
|
1548
|
+
patient_id: prescription['patient_id'],
|
|
1549
|
+
regimen_category: regimen,
|
|
1550
|
+
drugs: prescription['drugs'].split(',').collect(&:to_i),
|
|
1551
|
+
prescription_date: prescription['prescription_date']
|
|
1552
|
+
}
|
|
1553
|
+
end
|
|
1554
|
+
end
|
|
1555
|
+
|
|
1556
|
+
def filter_prescriptions_by_regimen(prescriptions, regimen)
|
|
1557
|
+
prescriptions.select do |prescription|
|
|
1558
|
+
prescription[:regimen_category].casecmp?(regimen)
|
|
1559
|
+
end
|
|
1560
|
+
end
|
|
1561
|
+
|
|
1562
|
+
def filter_prescriptions_by_drugs(prescriptions, drug_ids)
|
|
1563
|
+
prescriptions.select do |prescription|
|
|
1564
|
+
prescription[:drugs].find { |drug_id| drug_ids.include?(drug_id) }
|
|
1565
|
+
end
|
|
1566
|
+
end
|
|
1567
|
+
|
|
1568
|
+
def died_in(month_str)
|
|
1569
|
+
registered = []
|
|
1570
|
+
if month_str == '4+ months'
|
|
1571
|
+
data = ActiveRecord::Base.connection.select_all(
|
|
1572
|
+
"SELECT t.patient_id, #{function_manager(function: 'died_in', location: @location,
|
|
1573
|
+
args: "t.patient_id, cum_outcome, earliest_start_date, #{@location}")} died_in
|
|
1574
|
+
FROM temp_patient_outcomes o
|
|
1575
|
+
INNER JOIN temp_earliest_start_date t ON t.patient_id = o.patient_id #{site_manager(operator: 'AND',
|
|
1576
|
+
column: 't.site_id', location: @location)}
|
|
1577
|
+
WHERE cum_outcome = 'Patient died' #{site_manager(operator: 'AND', column: 'o.site_id',
|
|
1578
|
+
location: @location)}
|
|
1579
|
+
GROUP BY t.patient_id #{@adapter == 'mysql2' ? '' : ', cum_outcome, earliest_start_date'}
|
|
1580
|
+
HAVING LOWER(#{if @adapter == 'mysql2'
|
|
1581
|
+
'died_in'
|
|
1582
|
+
else
|
|
1583
|
+
function_manager(function: 'died_in', location: @location,
|
|
1584
|
+
args: "t.patient_id, cum_outcome, earliest_start_date, #{@location}")
|
|
1585
|
+
end}) IN (LOWER('4+ months'), LOWER('Unknown'))"
|
|
1586
|
+
)
|
|
1587
|
+
else
|
|
1588
|
+
data = ActiveRecord::Base.connection.select_all(
|
|
1589
|
+
"SELECT t.patient_id, #{function_manager(function: 'died_in', location: @location,
|
|
1590
|
+
args: "t.patient_id, cum_outcome, earliest_start_date, #{@location}")} died_in
|
|
1591
|
+
FROM temp_patient_outcomes o
|
|
1592
|
+
INNER JOIN temp_earliest_start_date t ON t.patient_id = o.patient_id #{site_manager(operator: 'AND',
|
|
1593
|
+
column: 't.site_id', location: @location)}
|
|
1594
|
+
WHERE cum_outcome = 'Patient died' #{site_manager(operator: 'AND', column: 'o.site_id',
|
|
1595
|
+
location: @location)}
|
|
1596
|
+
GROUP BY t.patient_id #{@adapter == 'mysql2' ? '' : ', cum_outcome, earliest_start_date'}
|
|
1597
|
+
HAVING LOWER(#{if @adapter == 'mysql2'
|
|
1598
|
+
'died_in'
|
|
1599
|
+
else
|
|
1600
|
+
function_manager(function: 'died_in', location: @location,
|
|
1601
|
+
args: "t.patient_id, cum_outcome, earliest_start_date, #{@location}")
|
|
1602
|
+
end}) = '#{month_str.downcase}'"
|
|
1603
|
+
)
|
|
1604
|
+
end
|
|
1605
|
+
|
|
1606
|
+
(data || []).each do |patient|
|
|
1607
|
+
registered << patient['patient_id']
|
|
1608
|
+
end
|
|
1609
|
+
|
|
1610
|
+
registered
|
|
1611
|
+
end
|
|
1612
|
+
|
|
1613
|
+
def get_outcome(outcome)
|
|
1614
|
+
sql_patch = if outcome.casecmp('Pre-ART (Continue)').zero?
|
|
1615
|
+
"LOWER(cum_outcome) = '#{outcome.downcase}' OR LOWER(cum_outcome) = LOWER('Unknown')"
|
|
1616
|
+
else
|
|
1617
|
+
"LOWER(cum_outcome) = '#{outcome.downcase}'"
|
|
1618
|
+
end
|
|
1619
|
+
|
|
1620
|
+
ActiveRecord::Base.connection.select_all(
|
|
1621
|
+
"SELECT * FROM temp_patient_outcomes WHERE #{sql_patch} #{site_manager(operator: 'AND', column: 'site_id',
|
|
1622
|
+
location: @location)} GROUP BY patient_id"
|
|
1623
|
+
)
|
|
1624
|
+
end
|
|
1625
|
+
|
|
1626
|
+
def kaposis_sarcoma(start_date, end_date)
|
|
1627
|
+
# KAPOSIS SARCOMA
|
|
1628
|
+
concept_id = concept_name('KAPOSIS SARCOMA').concept_id
|
|
1629
|
+
yes_concept_id = concept_name('Yes').concept_id
|
|
1630
|
+
who_stages_criteria = concept_name('Who stages criteria present').concept_id
|
|
1631
|
+
|
|
1632
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
|
1633
|
+
SELECT t.patient_id
|
|
1634
|
+
FROM temp_earliest_start_date t
|
|
1635
|
+
INNER JOIN obs ON t.patient_id = obs.person_id #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)} AND obs.voided = 0
|
|
1636
|
+
WHERE t.date_enrolled BETWEEN '#{start_date}' AND '#{end_date}'
|
|
1637
|
+
AND ((obs.value_coded = #{concept_id} AND obs.concept_id = #{who_stages_criteria}) OR (obs.concept_id = #{concept_id} AND obs.value_coded = #{yes_concept_id}))
|
|
1638
|
+
AND DATE(obs_datetime) <= DATE(date_enrolled) #{site_manager(operator: 'AND', column: 't.site_id', location: @location)}
|
|
1639
|
+
GROUP BY patient_id
|
|
1640
|
+
SQL
|
|
1641
|
+
end
|
|
1642
|
+
|
|
1643
|
+
def current_episode_of_tb(start_date, end_date)
|
|
1644
|
+
# CURRENT EPISODE OF TB
|
|
1645
|
+
eptb_concept_id = concept('EXTRAPULMONARY TUBERCULOSIS (EPTB)').concept_id
|
|
1646
|
+
yes_concept_id = concept('Yes').concept_id
|
|
1647
|
+
pulmonary_tb_concept_id = concept('PULMONARY TUBERCULOSIS').concept_id
|
|
1648
|
+
current_ptb_concept_id = concept('PULMONARY TUBERCULOSIS (CURRENT)').concept_id
|
|
1649
|
+
who_stages_criteria = concept('Who stages criteria present').concept_id
|
|
1650
|
+
|
|
1651
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
|
1652
|
+
SELECT t.patient_id
|
|
1653
|
+
FROM temp_earliest_start_date t
|
|
1654
|
+
INNER JOIN obs ON t.patient_id = obs.person_id #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
1655
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}'
|
|
1656
|
+
AND ( (#{in_manager(column: 'value_coded', values: [eptb_concept_id, pulmonary_tb_concept_id, current_ptb_concept_id])} AND concept_id = #{who_stages_criteria} )
|
|
1657
|
+
OR#{' '}
|
|
1658
|
+
(#{in_manager(column: 'concept_id', values: [eptb_concept_id, pulmonary_tb_concept_id, current_ptb_concept_id])} AND value_coded = #{yes_concept_id}))
|
|
1659
|
+
AND voided = 0 AND DATE(obs_datetime) <= DATE(date_enrolled) #{site_manager(operator: 'AND', column: 't.site_id', location: @location)}
|
|
1660
|
+
GROUP BY patient_id
|
|
1661
|
+
SQL
|
|
1662
|
+
end
|
|
1663
|
+
|
|
1664
|
+
def tb_within_the_last_two_years(patients_with_current_tb, start_date, end_date)
|
|
1665
|
+
# patients with current episode of tb
|
|
1666
|
+
patients_with_current_tb_episode = []
|
|
1667
|
+
(patients_with_current_tb || []).each do |patient|
|
|
1668
|
+
patients_with_current_tb_episode << patient['patient_id'].to_i
|
|
1669
|
+
end
|
|
1670
|
+
|
|
1671
|
+
patients_with_current_tb_episode = [0] if patients_with_current_tb_episode.blank?
|
|
1672
|
+
|
|
1673
|
+
# Pulmonary tuberculosis within the last 2 years
|
|
1674
|
+
pulmonary_tb_within_last_2yrs_concept_id = concept('Pulmonary tuberculosis within the last 2 years').concept_id
|
|
1675
|
+
ptb_within_the_past_two_yrs_concept_id = concept('Ptb within the past two years').concept_id
|
|
1676
|
+
who_stages_criteria = concept('Who stages criteria present').concept_id
|
|
1677
|
+
yes_concept_id = concept('Yes').concept_id
|
|
1678
|
+
|
|
1679
|
+
ActiveRecord::Base.connection.select_all(
|
|
1680
|
+
"SELECT t.patient_id
|
|
1681
|
+
FROM temp_earliest_start_date t
|
|
1682
|
+
INNER JOIN obs ON t.patient_id = obs.person_id #{site_manager(operator: 'AND', column: 'obs.site_id',
|
|
1683
|
+
location: @location)}
|
|
1684
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}'
|
|
1685
|
+
AND ((#{in_manager(column: 'value_coded',
|
|
1686
|
+
values: [pulmonary_tb_within_last_2yrs_concept_id,
|
|
1687
|
+
ptb_within_the_past_two_yrs_concept_id])}
|
|
1688
|
+
AND concept_id = #{who_stages_criteria})
|
|
1689
|
+
OR ( #{in_manager(column: 'concept_id',
|
|
1690
|
+
values: [pulmonary_tb_within_last_2yrs_concept_id,
|
|
1691
|
+
ptb_within_the_past_two_yrs_concept_id])} AND value_coded = #{yes_concept_id}))
|
|
1692
|
+
AND #{in_manager(column: 'patient_id', values: patients_with_current_tb_episode.join(','),
|
|
1693
|
+
negation: true)}
|
|
1694
|
+
AND voided = 0 AND DATE(obs_datetime) <= DATE(date_enrolled) #{site_manager(operator: 'AND',
|
|
1695
|
+
column: 't.site_id', location: @location)}
|
|
1696
|
+
GROUP BY patient_id"
|
|
1697
|
+
)
|
|
1698
|
+
end
|
|
1699
|
+
|
|
1700
|
+
def no_tb(total_registered, tb_within_the_last_two_years, current_episode_of_tb)
|
|
1701
|
+
total_registered_patients = []
|
|
1702
|
+
tb_within_2yrs_patients = []
|
|
1703
|
+
current_tb_episode_patients = []
|
|
1704
|
+
|
|
1705
|
+
(total_registered || []).each do |patient|
|
|
1706
|
+
total_registered_patients << patient['patient_id'].to_i
|
|
1707
|
+
end
|
|
1708
|
+
|
|
1709
|
+
(tb_within_the_last_two_years || []).each do |patient|
|
|
1710
|
+
tb_within_2yrs_patients << patient['patient_id'].to_i
|
|
1711
|
+
end
|
|
1712
|
+
|
|
1713
|
+
(current_episode_of_tb || []).each do |patient|
|
|
1714
|
+
current_tb_episode_patients << patient['patient_id'].to_i
|
|
1715
|
+
end
|
|
1716
|
+
|
|
1717
|
+
total_registered_patients - (tb_within_2yrs_patients + current_tb_episode_patients)
|
|
1718
|
+
end
|
|
1719
|
+
|
|
1720
|
+
def cum_no_tb(cum_total_registered, cum_tb_within_the_last_two_years, cum_current_episode_of_tb)
|
|
1721
|
+
total_registered_patients = []
|
|
1722
|
+
tb_within_2yrs_patients = []
|
|
1723
|
+
current_tb_episode_patients = []
|
|
1724
|
+
|
|
1725
|
+
(cum_total_registered || []).each do |patient|
|
|
1726
|
+
total_registered_patients << patient['patient_id'].to_i
|
|
1727
|
+
end
|
|
1728
|
+
|
|
1729
|
+
(cum_tb_within_the_last_two_years || []).each do |patient|
|
|
1730
|
+
tb_within_2yrs_patients << patient['patient_id'].to_i
|
|
1731
|
+
end
|
|
1732
|
+
|
|
1733
|
+
(cum_current_episode_of_tb || []).each do |patient|
|
|
1734
|
+
current_tb_episode_patients << patient['patient_id'].to_i
|
|
1735
|
+
end
|
|
1736
|
+
|
|
1737
|
+
total_registered_patients - (tb_within_2yrs_patients + current_tb_episode_patients)
|
|
1738
|
+
end
|
|
1739
|
+
|
|
1740
|
+
def children_12_59_months(start_date, end_date)
|
|
1741
|
+
concept = ::ConceptName.where('LOWER(name) IN (?)', 'HIV Infected'.downcase).select(:concept_id)
|
|
1742
|
+
|
|
1743
|
+
find_patients_by_reason_for_starting(start_date, end_date, concept)
|
|
1744
|
+
end
|
|
1745
|
+
|
|
1746
|
+
def unknown_other_reason_outside_guidelines(start_date, end_date)
|
|
1747
|
+
# All WHO stage 1 and 2 patients that were enrolled before '2016-04-01'
|
|
1748
|
+
# should be included in this group.
|
|
1749
|
+
unknown_concepts = ::ConceptName.where('LOWER(name) IN (?)', ['Unknown'.downcase, 'None'.downcase])
|
|
1750
|
+
.select(:concept_id)
|
|
1751
|
+
.to_sql
|
|
1752
|
+
|
|
1753
|
+
if start_date.to_date > '2016-04-01'.to_date
|
|
1754
|
+
return ActiveRecord::Base.connection.select_all <<~SQL
|
|
1755
|
+
SELECT patient_id
|
|
1756
|
+
FROM temp_earliest_start_date
|
|
1757
|
+
WHERE reason_for_starting_art IN (#{unknown_concepts})
|
|
1758
|
+
AND date_enrolled >= '#{start_date}'
|
|
1759
|
+
AND date_enrolled <= '#{end_date}' #{site_manager(operator: 'AND', column: 'site_id', location: @location)}
|
|
1760
|
+
SQL
|
|
1761
|
+
end
|
|
1762
|
+
|
|
1763
|
+
stage_1_and_2_concept_names = ['LYMPHOCYTE COUNT BELOW THRESHOLD WITH WHO STAGE 1'.downcase,
|
|
1764
|
+
'LYMPHOCYTES'.downcase,
|
|
1765
|
+
'LYMPHOCYTE COUNT BELOW THRESHOLD WITH WHO STAGE 2'.downcase,
|
|
1766
|
+
'WHO stage I adult'.downcase,
|
|
1767
|
+
'WHO stage I peds'.downcase,
|
|
1768
|
+
'WHO STAGE 1'.downcase,
|
|
1769
|
+
'WHO stage II adult'.downcase,
|
|
1770
|
+
'WHO stage II peds'.downcase,
|
|
1771
|
+
'WHO STAGE 2'.downcase]
|
|
1772
|
+
|
|
1773
|
+
stage_1_and_2_concepts = ::ConceptName.where('LOWER(name) IN (?)', stage_1_and_2_concept_names)
|
|
1774
|
+
.select('DISTINCT concept_id')
|
|
1775
|
+
.to_sql
|
|
1776
|
+
|
|
1777
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
|
1778
|
+
SELECT patient_id
|
|
1779
|
+
FROM temp_earliest_start_date
|
|
1780
|
+
WHERE
|
|
1781
|
+
(
|
|
1782
|
+
reason_for_starting_art IN (#{unknown_concepts})
|
|
1783
|
+
AND date_enrolled >= '#{start_date}'
|
|
1784
|
+
AND date_enrolled <= '#{end_date}'
|
|
1785
|
+
)
|
|
1786
|
+
OR (
|
|
1787
|
+
reason_for_starting_art IN (#{stage_1_and_2_concepts})
|
|
1788
|
+
AND date_enrolled <= DATE('2016-04-01')
|
|
1789
|
+
) #{site_manager(operator: 'AND', column: 'site_id', location: @location)}
|
|
1790
|
+
SQL
|
|
1791
|
+
end
|
|
1792
|
+
|
|
1793
|
+
def who_stage_four(start_date, end_date)
|
|
1794
|
+
concepts = ::ConceptName.where('LOWER(name) IN (?)',
|
|
1795
|
+
['WHO stage IV adult'.downcase, 'WHO stage IV peds'.downcase,
|
|
1796
|
+
'WHO STAGE 4'.downcase])
|
|
1797
|
+
.select(:concept_id)
|
|
1798
|
+
find_patients_by_reason_for_starting(start_date, end_date, concepts)
|
|
1799
|
+
end
|
|
1800
|
+
|
|
1801
|
+
def who_stage_three(start_date, end_date)
|
|
1802
|
+
concepts = ::ConceptName.where('LOWER(name) IN (?)',
|
|
1803
|
+
['WHO stage III adult'.downcase, 'WHO stage III peds'.downcase,
|
|
1804
|
+
'WHO STAGE 3'.downcase])
|
|
1805
|
+
.select(:concept_id)
|
|
1806
|
+
find_patients_by_reason_for_starting(start_date, end_date, concepts)
|
|
1807
|
+
end
|
|
1808
|
+
|
|
1809
|
+
def pregnant_women(start_date, end_date)
|
|
1810
|
+
concepts = ::ConceptName.where('LOWER(name) IN (?)', ['PATIENT PREGNANT'.downcase, 'Is patient pregnant at initiation?'.downcase,
|
|
1811
|
+
'Patient pregnant state'.downcase, 'Is patient pregnant?'.downcase])
|
|
1812
|
+
.select(:concept_id)
|
|
1813
|
+
find_patients_by_reason_for_starting(start_date, end_date, concepts)
|
|
1814
|
+
end
|
|
1815
|
+
|
|
1816
|
+
def breastfeeding_mothers(start_date, end_date)
|
|
1817
|
+
concept = ::ConceptName.where('LOWER(name) IN (?)', 'Breastfeeding'.downcase).select(:concept_id)
|
|
1818
|
+
find_patients_by_reason_for_starting(start_date, end_date, concept)
|
|
1819
|
+
end
|
|
1820
|
+
|
|
1821
|
+
def asymptomatic(start_date, end_date)
|
|
1822
|
+
# for WHO stage 1 and 2 to be included in asymptomatic, the patients are supposed to
|
|
1823
|
+
# be enrolled on HIV PROGRAM after 2016-04-01
|
|
1824
|
+
patients = []
|
|
1825
|
+
|
|
1826
|
+
asymptomatic_concepts = ::ConceptName.where('LOWER(name) IN (?)',
|
|
1827
|
+
['ASYMPTOMATIC'.downcase, 'Asymptomatic HIV infection'.downcase])
|
|
1828
|
+
.select(:concept_id)
|
|
1829
|
+
find_patients_by_reason_for_starting(start_date, end_date, asymptomatic_concepts)
|
|
1830
|
+
.each { |patient| patients << patient['patient_id'] }
|
|
1831
|
+
|
|
1832
|
+
reason_concepts = ::ConceptName.where(name: ['WHO stage I adult'.downcase,
|
|
1833
|
+
'WHO stage I peds'.downcase,
|
|
1834
|
+
'WHO stage 1'.downcase,
|
|
1835
|
+
'WHO stage II adult'.downcase,
|
|
1836
|
+
'WHO stage II peds'.downcase,
|
|
1837
|
+
'LYMPHOCYTE COUNT BELOW THRESHOLD WITH WHO STAGE 1'.downcase,
|
|
1838
|
+
'LYMPHOCYTES'.downcase,
|
|
1839
|
+
'LYMPHOCYTE COUNT BELOW THRESHOLD WITH WHO STAGE 2'.downcase])
|
|
1840
|
+
.select(:concept_id)
|
|
1841
|
+
|
|
1842
|
+
revised_art_guidelines_date = '2016-04-01'.to_date
|
|
1843
|
+
start_date = revised_art_guidelines_date if start_date.to_date < revised_art_guidelines_date
|
|
1844
|
+
|
|
1845
|
+
find_patients_by_reason_for_starting(start_date, end_date, reason_concepts)
|
|
1846
|
+
.each { |patient| patients << patient['patient_id'] }
|
|
1847
|
+
|
|
1848
|
+
patients
|
|
1849
|
+
end
|
|
1850
|
+
|
|
1851
|
+
def who_stage_two(start_date, end_date)
|
|
1852
|
+
concepts = ::ConceptName.where('LOWER(name) IN (?)', ['CD4 COUNT LESS THAN OR EQUAL TO 750'.downcase,
|
|
1853
|
+
'CD4 count less than or equal to 500'.downcase,
|
|
1854
|
+
'CD4 COUNT LESS THAN OR EQUAL TO 350'.downcase,
|
|
1855
|
+
'CD4 count less than 350'.downcase,
|
|
1856
|
+
'CD4 count less than 250'.downcase,
|
|
1857
|
+
'CD4 COUNT LESS THAN OR EQUAL TO 250'.downcase])
|
|
1858
|
+
.select(:concept_id)
|
|
1859
|
+
find_patients_by_reason_for_starting(start_date, end_date, concepts)
|
|
1860
|
+
end
|
|
1861
|
+
|
|
1862
|
+
def confirmed_hiv_infection_in_infants_pcr(start_date, end_date)
|
|
1863
|
+
concept = ::ConceptName.where('LOWER(name) IN (?)', 'HIV PCR'.downcase).select(:concept_id)
|
|
1864
|
+
find_patients_by_reason_for_starting(start_date, end_date, concept)
|
|
1865
|
+
end
|
|
1866
|
+
|
|
1867
|
+
def presumed_severe_hiv_disease_in_infants(start_date, end_date)
|
|
1868
|
+
concepts = ::ConceptName.where('LOWER(name) IN (?)', ['PRESUMED SEVERE HIV'.downcase,
|
|
1869
|
+
'PRESUMED SEVERE HIV CRITERIA IN INFANTS'.downcase])
|
|
1870
|
+
.select(:concept_id)
|
|
1871
|
+
find_patients_by_reason_for_starting(start_date, end_date, concepts)
|
|
1872
|
+
end
|
|
1873
|
+
|
|
1874
|
+
def find_patients_by_reason_for_starting(start_date, end_date, reason_concept_ids)
|
|
1875
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
|
1876
|
+
SELECT patient_id
|
|
1877
|
+
FROM temp_earliest_start_date
|
|
1878
|
+
WHERE date_enrolled >= '#{start_date}'
|
|
1879
|
+
AND date_enrolled <= '#{end_date}'
|
|
1880
|
+
AND reason_for_starting_art IN (#{reason_concept_ids.to_sql}) #{site_manager(operator: 'AND', column: 'site_id', location: @location)}
|
|
1881
|
+
SQL
|
|
1882
|
+
end
|
|
1883
|
+
|
|
1884
|
+
def unknown_age(start_date, end_date)
|
|
1885
|
+
ActiveRecord::Base.connection.select_all(
|
|
1886
|
+
"SELECT patient_id
|
|
1887
|
+
FROM temp_earliest_start_date
|
|
1888
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}' #{site_manager(operator: 'AND',
|
|
1889
|
+
column: 'site_id', location: @location)}
|
|
1890
|
+
AND (age_at_initiation IS NULL OR age_at_initiation < 0 OR birthdate IS NULL)
|
|
1891
|
+
GROUP BY patient_id"
|
|
1892
|
+
)
|
|
1893
|
+
end
|
|
1894
|
+
|
|
1895
|
+
def unknown_gender(start_date, end_date)
|
|
1896
|
+
ActiveRecord::Base.connection.select_all(
|
|
1897
|
+
"SELECT patient_id
|
|
1898
|
+
FROM temp_earliest_start_date
|
|
1899
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}' #{site_manager(operator: 'AND',
|
|
1900
|
+
column: 'site_id', location: @location)}
|
|
1901
|
+
AND gender IS NULL OR LENGTH(gender) < 1 GROUP BY patient_id;"
|
|
1902
|
+
)
|
|
1903
|
+
end
|
|
1904
|
+
|
|
1905
|
+
def adults_at_art_initiation(start_date, end_date)
|
|
1906
|
+
ActiveRecord::Base.connection.select_all(
|
|
1907
|
+
"SELECT patient_id FROM temp_earliest_start_date
|
|
1908
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}' #{site_manager(operator: 'AND',
|
|
1909
|
+
column: 'site_id', location: @location)}
|
|
1910
|
+
AND age_at_initiation > 14 GROUP BY patient_id"
|
|
1911
|
+
)
|
|
1912
|
+
end
|
|
1913
|
+
|
|
1914
|
+
def children_24_months_14_years_at_art_initiation(start_date, end_date)
|
|
1915
|
+
ActiveRecord::Base.connection.select_all(
|
|
1916
|
+
"SELECT patient_id FROM temp_earliest_start_date
|
|
1917
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}' #{site_manager(operator: 'AND',
|
|
1918
|
+
column: 'site_id', location: @location)}
|
|
1919
|
+
AND age_at_initiation BETWEEN 2 AND 14 GROUP BY patient_id"
|
|
1920
|
+
)
|
|
1921
|
+
end
|
|
1922
|
+
|
|
1923
|
+
def children_below_24_months_at_art_initiation(start_date, end_date)
|
|
1924
|
+
ActiveRecord::Base.connection.select_all(
|
|
1925
|
+
"SELECT patient_id FROM temp_earliest_start_date
|
|
1926
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}' #{site_manager(operator: 'AND',
|
|
1927
|
+
column: 'site_id', location: @location)}
|
|
1928
|
+
AND (age_at_initiation >= 0 AND age_at_initiation < 2) GROUP BY patient_id"
|
|
1929
|
+
)
|
|
1930
|
+
end
|
|
1931
|
+
|
|
1932
|
+
def non_pregnant_females(start_date, end_date, pregnant_women = [])
|
|
1933
|
+
pregnant_women_ids = []
|
|
1934
|
+
(pregnant_women || []).each do |patient|
|
|
1935
|
+
pregnant_women_ids << patient
|
|
1936
|
+
end
|
|
1937
|
+
pregnant_women_ids = [0] if pregnant_women_ids.blank?
|
|
1938
|
+
|
|
1939
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
|
1940
|
+
SELECT t.patient_id
|
|
1941
|
+
FROM temp_earliest_start_date t
|
|
1942
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}' #{site_manager(operator: 'AND', column: 't.site_id', location: @location)}
|
|
1943
|
+
AND (t.gender = 'F' OR t.gender = 'Female')
|
|
1944
|
+
AND t.patient_id NOT IN(#{pregnant_women_ids.join(',')})
|
|
1945
|
+
GROUP BY t.patient_id
|
|
1946
|
+
SQL
|
|
1947
|
+
end
|
|
1948
|
+
|
|
1949
|
+
def pregnant_females_all_ages(start_date, end_date)
|
|
1950
|
+
yes_concept_id = concept('Yes').concept_id
|
|
1951
|
+
preg_concept_id = concept('IS PATIENT PREGNANT?').concept_id
|
|
1952
|
+
patient_preg_concept_id = concept('PATIENT PREGNANT').concept_id
|
|
1953
|
+
preg_at_initiation_concept_id = concept('PREGNANT AT INITIATION?').concept_id
|
|
1954
|
+
reason_for_starting_concept_id = concept('Reason for ART eligibility').concept_id
|
|
1955
|
+
|
|
1956
|
+
# (patient_id_plus_date_enrolled || []).each do |patient_id, date_enrolled|
|
|
1957
|
+
registered = ActiveRecord::Base.connection.select_all <<~SQL
|
|
1958
|
+
SELECT patients.*, obs.value_coded
|
|
1959
|
+
FROM temp_earliest_start_date AS patients
|
|
1960
|
+
INNER JOIN obs
|
|
1961
|
+
ON obs.person_id = patients.patient_id
|
|
1962
|
+
AND #{in_manager(column: 'obs.concept_id', values: [preg_concept_id, patient_preg_concept_id, preg_at_initiation_concept_id, reason_for_starting_concept_id])}
|
|
1963
|
+
AND obs.obs_datetime >= patients.earliest_start_date
|
|
1964
|
+
AND obs.obs_datetime < #{interval_manager(date: 'patients.earliest_start_date', value: 1, interval: 'DAY', operator: '+')}
|
|
1965
|
+
AND #{in_manager(column: 'obs.value_coded', values: [yes_concept_id, patient_preg_concept_id])}
|
|
1966
|
+
AND obs.voided = 0 #{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
|
1967
|
+
WHERE patients.gender IN ('F','Female')
|
|
1968
|
+
AND patients.date_enrolled BETWEEN '#{start_date}' AND '#{end_date}' #{site_manager(operator: 'AND', column: 'patients.site_id', location: @location)}
|
|
1969
|
+
GROUP BY patient_id #{@adapter == 'mysql2' ? '' : ',obs.value_coded'}
|
|
1970
|
+
SQL
|
|
1971
|
+
|
|
1972
|
+
pregnant_at_initiation = ActiveRecord::Base.connection.select_all <<~SQL
|
|
1973
|
+
SELECT patient_id, #{function_manager(function: 'patient_reason_for_starting_art', location: @location,
|
|
1974
|
+
args: "patient_id, #{@location}")} reason_concept_id
|
|
1975
|
+
FROM temp_earliest_start_date
|
|
1976
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}'
|
|
1977
|
+
AND gender IN ('F','Female') #{site_manager(operator: 'AND', column: 'site_id', location: @location)}
|
|
1978
|
+
GROUP BY patient_id
|
|
1979
|
+
HAVING #{if @adapter == 'mysql2'
|
|
1980
|
+
'reason_concept_id IN (1755, 7972, 6131)'
|
|
1981
|
+
else
|
|
1982
|
+
in_manager(column: function_manager(function: 'patient_reason_for_starting_art', location: @location, args: "patient_id, #{@location}").to_s,
|
|
1983
|
+
values: [
|
|
1984
|
+
1755, 7972, 6131
|
|
1985
|
+
])
|
|
1986
|
+
end};
|
|
1987
|
+
SQL
|
|
1988
|
+
pregnant_at_initiation_ids = []
|
|
1989
|
+
(pregnant_at_initiation || []).each do |patient|
|
|
1990
|
+
pregnant_at_initiation_ids << patient['patient_id'].to_i
|
|
1991
|
+
end
|
|
1992
|
+
|
|
1993
|
+
pregnant_at_initiation_ids = [0] if pregnant_at_initiation_ids.blank?
|
|
1994
|
+
|
|
1995
|
+
transfer_ins_women = ActiveRecord::Base.connection.select_all <<~SQL
|
|
1996
|
+
SELECT patient_id, #{function_manager(function: 're_initiated_check', location: @location,
|
|
1997
|
+
args: "patient_id, date_enrolled, #{@location}")} re_initiated
|
|
1998
|
+
FROM temp_earliest_start_date
|
|
1999
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}'
|
|
2000
|
+
AND DATE(date_enrolled) != DATE(earliest_start_date)
|
|
2001
|
+
AND gender IN ('F','Female') #{site_manager(operator: 'AND', column: 'site_id', location: @location)}
|
|
2002
|
+
AND #{in_manager(column: 'patient_id', values: pregnant_at_initiation_ids)}
|
|
2003
|
+
GROUP BY patient_id
|
|
2004
|
+
HAVING #{ if @adapter == 'mysql2'
|
|
2005
|
+
're_initiated'
|
|
2006
|
+
else
|
|
2007
|
+
function_manager(function: 're_initiated_check', location: @location,
|
|
2008
|
+
args: "patient_id, date_enrolled, #{@location}")
|
|
2009
|
+
end} != 'Re-initiated'
|
|
2010
|
+
SQL
|
|
2011
|
+
|
|
2012
|
+
transfer_ins_preg_women = []
|
|
2013
|
+
all_pregnant_females = []
|
|
2014
|
+
(transfer_ins_women || []).each do |patient|
|
|
2015
|
+
transfer_ins_preg_women << patient['patient_id'].to_i if patient['patient_id'].to_i != 0
|
|
2016
|
+
end
|
|
2017
|
+
|
|
2018
|
+
(registered || []).each do |patient|
|
|
2019
|
+
all_pregnant_females << patient['patient_id'].to_i if patient['patient_id'].to_i != 0
|
|
2020
|
+
end
|
|
2021
|
+
|
|
2022
|
+
(all_pregnant_females + transfer_ins_preg_women).uniq
|
|
2023
|
+
end
|
|
2024
|
+
|
|
2025
|
+
def initial_females_all_ages(start_date, end_date, data)
|
|
2026
|
+
clients = []
|
|
2027
|
+
women = ActiveRecord::Base.connection.select_all("
|
|
2028
|
+
SELECT * FROM temp_earliest_start_date e
|
|
2029
|
+
WHERE patient_id IN(#{data.length.positive? ? data.join(',') : 0}) #{site_manager(operator: 'AND',
|
|
2030
|
+
column: 'e.site_id', location: @location)}
|
|
2031
|
+
AND date_enrolled BETWEEN '#{start_date.to_date}' AND '#{end_date.to_date}'
|
|
2032
|
+
AND DATE(date_enrolled) = DATE(earliest_start_date);")
|
|
2033
|
+
|
|
2034
|
+
(women || []).each do |w|
|
|
2035
|
+
clients << w
|
|
2036
|
+
end
|
|
2037
|
+
|
|
2038
|
+
clients
|
|
2039
|
+
end
|
|
2040
|
+
|
|
2041
|
+
def males(start_date, end_date)
|
|
2042
|
+
ActiveRecord::Base.connection.select_all(
|
|
2043
|
+
"SELECT * FROM temp_earliest_start_date t
|
|
2044
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}' #{site_manager(operator: 'AND',
|
|
2045
|
+
column: 'site_id', location: @location)}
|
|
2046
|
+
AND (gender = 'Male' OR gender = 'M') GROUP BY patient_id"
|
|
2047
|
+
)
|
|
2048
|
+
end
|
|
2049
|
+
|
|
2050
|
+
def transfer_in(start_date, end_date, re_initiated_on_art)
|
|
2051
|
+
start_date = ActiveRecord::Base.connection.quote(start_date)
|
|
2052
|
+
end_date = ActiveRecord::Base.connection.quote(end_date)
|
|
2053
|
+
|
|
2054
|
+
re_initiated_on_art = re_initiated_on_art.empty? ? [0] : re_initiated_on_art.rows.collect(&:first)
|
|
2055
|
+
|
|
2056
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
|
2057
|
+
SELECT temp_earliest_start_date.patient_id
|
|
2058
|
+
FROM temp_earliest_start_date
|
|
2059
|
+
INNER JOIN clinic_registration_encounter
|
|
2060
|
+
ON clinic_registration_encounter.patient_id = temp_earliest_start_date.patient_id #{site_manager(operator: 'AND', column: 'clinic_registration_encounter.site_id', location: @location)}
|
|
2061
|
+
LEFT JOIN ever_registered_obs
|
|
2062
|
+
ON ever_registered_obs.person_id = temp_earliest_start_date.patient_id #{site_manager(operator: 'AND', column: 'ever_registered_obs.site_id', location: @location)}
|
|
2063
|
+
AND ever_registered_obs.value_coded = (
|
|
2064
|
+
SELECT concept_id FROM concept_name WHERE LOWER(name) = LOWER('Yes') AND voided = 0 LIMIT 1
|
|
2065
|
+
)
|
|
2066
|
+
LEFT JOIN (
|
|
2067
|
+
SELECT person_id, MIN(obs_datetime) AS obs_datetime
|
|
2068
|
+
FROM ever_registered_obs #{site_manager(operator: 'WHERE', column: 'site_id', location: @location)}
|
|
2069
|
+
GROUP BY person_id
|
|
2070
|
+
) AS max_ever_registered_obs
|
|
2071
|
+
ON max_ever_registered_obs.person_id = ever_registered_obs.person_id
|
|
2072
|
+
AND max_ever_registered_obs.obs_datetime = ever_registered_obs.obs_datetime
|
|
2073
|
+
LEFT JOIN obs AS last_taken_art_obs
|
|
2074
|
+
ON last_taken_art_obs.encounter_id = ever_registered_obs.encounter_id
|
|
2075
|
+
AND last_taken_art_obs.voided = 0 #{site_manager(operator: 'AND', column: 'last_taken_art_obs.site_id', location: @location)}
|
|
2076
|
+
AND last_taken_art_obs.concept_id = (
|
|
2077
|
+
SELECT concept_id FROM concept_name WHERE LOWER(name) = LOWER('DATE ART LAST TAKEN') LIMIT 1
|
|
2078
|
+
)
|
|
2079
|
+
WHERE (date_enrolled BETWEEN #{start_date} AND #{end_date})
|
|
2080
|
+
AND date_enrolled != earliest_start_date #{site_manager(operator: 'AND', column: 'temp_earliest_start_date.site_id', location: @location)}
|
|
2081
|
+
AND COALESCE(#{
|
|
2082
|
+
if @adapter == 'mysql2'
|
|
2083
|
+
<<~SQL
|
|
2084
|
+
TIMESTAMPDIFF(day,last_taken_art_obs.value_datetime,last_taken_art_obs.obs_datetime) <= 14
|
|
2085
|
+
SQL
|
|
2086
|
+
else
|
|
2087
|
+
<<~SQL
|
|
2088
|
+
EXTRACT(DAY FROM AGE(last_taken_art_obs.obs_datetime::DATE, last_taken_art_obs.value_datetime::DATE)) <= 14
|
|
2089
|
+
SQL
|
|
2090
|
+
end
|
|
2091
|
+
}, TRUE)
|
|
2092
|
+
AND #{in_manager(column: 'temp_earliest_start_date.patient_id', values: re_initiated_on_art, negation: true)}
|
|
2093
|
+
GROUP BY temp_earliest_start_date.patient_id;
|
|
2094
|
+
SQL
|
|
2095
|
+
end
|
|
2096
|
+
|
|
2097
|
+
def re_initiated_on_art(start_date, end_date)
|
|
2098
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
|
2099
|
+
SELECT temp_earliest_start_date.patient_id
|
|
2100
|
+
FROM temp_earliest_start_date
|
|
2101
|
+
INNER JOIN clinic_registration_encounter
|
|
2102
|
+
ON temp_earliest_start_date.patient_id = clinic_registration_encounter.patient_id #{site_manager(operator: 'AND', column: 'clinic_registration_encounter.site_id', location: @location)}
|
|
2103
|
+
INNER JOIN ever_registered_obs
|
|
2104
|
+
ON clinic_registration_encounter.encounter_id = ever_registered_obs.encounter_id
|
|
2105
|
+
AND ever_registered_obs.value_coded = (SELECT concept_id FROM concept_name
|
|
2106
|
+
WHERE LOWER(name) = LOWER('Yes') AND voided = 0 LIMIT 1) #{site_manager(operator: 'AND', column: 'ever_registered_obs.site_id', location: @location)}
|
|
2107
|
+
INNER JOIN (
|
|
2108
|
+
SELECT person_id, MIN(obs_datetime) AS obs_datetime
|
|
2109
|
+
FROM ever_registered_obs #{site_manager(operator: 'WHERE', column: 'site_id', location: @location)}
|
|
2110
|
+
GROUP BY person_id
|
|
2111
|
+
) AS max_ever_registered_obs
|
|
2112
|
+
ON max_ever_registered_obs.person_id = ever_registered_obs.person_id
|
|
2113
|
+
AND max_ever_registered_obs.obs_datetime = ever_registered_obs.obs_datetime
|
|
2114
|
+
INNER JOIN obs AS last_taken_art_obs
|
|
2115
|
+
ON last_taken_art_obs.encounter_id = clinic_registration_encounter.encounter_id
|
|
2116
|
+
AND last_taken_art_obs.voided = 0 #{site_manager(operator: 'AND', column: 'last_taken_art_obs.site_id', location: @location)}
|
|
2117
|
+
AND last_taken_art_obs.concept_id = (
|
|
2118
|
+
SELECT concept_id FROM concept_name WHERE LOWER(name) = LOWER('DATE ART LAST TAKEN') LIMIT 1
|
|
2119
|
+
)
|
|
2120
|
+
WHERE (date_enrolled BETWEEN '#{start_date}' AND '#{end_date}')
|
|
2121
|
+
AND #{timestampdiff_manager(date1: 'last_taken_art_obs.value_datetime', date2: 'last_taken_art_obs.obs_datetime', interval: 'day')} > 14
|
|
2122
|
+
AND date_enrolled != earliest_start_date #{site_manager(operator: 'AND', column: 'temp_earliest_start_date.site_id', location: @location)}
|
|
2123
|
+
GROUP BY temp_earliest_start_date.patient_id;
|
|
2124
|
+
SQL
|
|
2125
|
+
end
|
|
2126
|
+
|
|
2127
|
+
def initiated_on_art_first_time(start_date, end_date)
|
|
2128
|
+
ActiveRecord::Base.connection.select_all(
|
|
2129
|
+
"SELECT * FROM temp_earliest_start_date
|
|
2130
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}'
|
|
2131
|
+
AND date_enrolled = earliest_start_date #{site_manager(operator: 'AND', column: 'site_id',
|
|
2132
|
+
location: @location)}
|
|
2133
|
+
GROUP BY patient_id"
|
|
2134
|
+
)
|
|
2135
|
+
end
|
|
2136
|
+
|
|
2137
|
+
def males_initiated_on_art_first_time(start_date, end_date, data)
|
|
2138
|
+
clients = []
|
|
2139
|
+
(data || []).each do |e|
|
|
2140
|
+
gender = e['gender']&.upcase&.first
|
|
2141
|
+
next if gender.blank?
|
|
2142
|
+
next unless gender == 'M'
|
|
2143
|
+
|
|
2144
|
+
date_enrolled = e['date_enrolled'].to_date
|
|
2145
|
+
start_date = start_date.to_date
|
|
2146
|
+
end_date = end_date.to_date
|
|
2147
|
+
date_enrolled >= start_date && date_enrolled <= end_date ? clients << e : next
|
|
2148
|
+
end
|
|
2149
|
+
|
|
2150
|
+
clients
|
|
2151
|
+
end
|
|
2152
|
+
|
|
2153
|
+
def get_cum_start_date
|
|
2154
|
+
cum_start_date = ActiveRecord::Base.connection.select_value <<~SQL
|
|
2155
|
+
SELECT MIN(date_enrolled) FROM temp_earliest_start_date #{site_manager(operator: 'WHERE', column: 'site_id', location: @location)}
|
|
2156
|
+
SQL
|
|
2157
|
+
|
|
2158
|
+
begin
|
|
2159
|
+
cum_start_date.to_date
|
|
2160
|
+
rescue StandardError
|
|
2161
|
+
nil
|
|
2162
|
+
end
|
|
2163
|
+
end
|
|
2164
|
+
|
|
2165
|
+
def total_registered(start_date, end_date)
|
|
2166
|
+
ActiveRecord::Base.connection.select_all(
|
|
2167
|
+
"SELECT * FROM temp_earliest_start_date
|
|
2168
|
+
WHERE date_enrolled BETWEEN '#{start_date}' AND '#{end_date}' #{site_manager(operator: 'AND',
|
|
2169
|
+
column: 'site_id', location: @location)}
|
|
2170
|
+
GROUP BY patient_id"
|
|
2171
|
+
)
|
|
2172
|
+
end
|
|
2173
|
+
|
|
2174
|
+
def load_tmp_patient_table(_cohort_struct)
|
|
2175
|
+
create_tmp_patient_table
|
|
2176
|
+
# arv_orders.each_with_object({}) do |order, patient_tab|
|
|
2177
|
+
# next if patient_tab.include?(order.patient_id)
|
|
2178
|
+
#
|
|
2179
|
+
# person = ::Person.find(order.patient_id)
|
|
2180
|
+
# next unless person.birthdate # && patient_in_program?(person.patient)
|
|
2181
|
+
#
|
|
2182
|
+
# add_patient_record(person, order, cohort_struct)
|
|
2183
|
+
#
|
|
2184
|
+
# patient_tab[order.patient_id] = person
|
|
2185
|
+
# end
|
|
2186
|
+
end
|
|
2187
|
+
|
|
2188
|
+
def create_tmp_patient_table_2(_end_date)
|
|
2189
|
+
##########################################################
|
|
2190
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
2191
|
+
DROP FUNCTION IF EXISTS patient_date_enrolled;
|
|
2192
|
+
SQL
|
|
2193
|
+
|
|
2194
|
+
::Drug.arv_drugs.map(&:concept_id)
|
|
2195
|
+
|
|
2196
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
2197
|
+
CREATE FUNCTION patient_date_enrolled(my_patient_id int) RETURNS DATE
|
|
2198
|
+
DETERMINISTIC
|
|
2199
|
+
BEGIN
|
|
2200
|
+
DECLARE my_start_date DATE;
|
|
2201
|
+
DECLARE min_start_date DATETIME;
|
|
2202
|
+
DECLARE arv_concept_id INT(11);
|
|
2203
|
+
|
|
2204
|
+
SET arv_concept_id = (SELECT concept_id FROM concept_name WHERE name ='ANTIRETROVIRAL DRUGS' LIMIT 1);
|
|
2205
|
+
|
|
2206
|
+
SET my_start_date = (SELECT DATE(o.start_date) FROM drug_order d INNER JOIN orders o ON d.order_id = o.order_id AND o.voided = 0 WHERE o.patient_id = my_patient_id AND drug_inventory_id IN(SELECT drug_id FROM drug WHERE concept_id IN(SELECT concept_id FROM concept_set WHERE concept_set = arv_concept_id)) AND d.quantity > 0 AND o.start_date = (SELECT min(start_date) FROM drug_order d INNER JOIN orders o ON d.order_id = o.order_id AND o.voided = 0 WHERE d.quantity > 0 AND o.patient_id = my_patient_id AND drug_inventory_id IN(SELECT drug_id FROM drug WHERE concept_id IN(SELECT concept_id FROM concept_set WHERE concept_set = arv_concept_id))) LIMIT 1);
|
|
2207
|
+
|
|
2208
|
+
|
|
2209
|
+
RETURN my_start_date;
|
|
2210
|
+
END;
|
|
2211
|
+
SQL
|
|
2212
|
+
##########################################################
|
|
2213
|
+
|
|
2214
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
2215
|
+
DROP TABLE IF EXISTS `temp_earliest_start_date`;
|
|
2216
|
+
SQL
|
|
2217
|
+
|
|
2218
|
+
ActiveRecord::Base.connection.execute <<~SQL
|
|
2219
|
+
CREATE TABLE temp_earliest_start_date
|
|
2220
|
+
select
|
|
2221
|
+
`p`.`patient_id` AS `patient_id`,
|
|
2222
|
+
`pe`.`gender` AS `gender`,
|
|
2223
|
+
`pe`.`birthdate`,
|
|
2224
|
+
date_antiretrovirals_started(`p`.`patient_id`, min(`s`.`start_date`)) AS `earliest_start_date`,
|
|
2225
|
+
cast(patient_date_enrolled(`p`.`patient_id`) as date) AS `date_enrolled`,
|
|
2226
|
+
`person`.`death_date` AS `death_date`,
|
|
2227
|
+
(select timestampdiff(year, `pe`.`birthdate`, min(`s`.`start_date`))) AS `age_at_initiation`,
|
|
2228
|
+
(select timestampdiff(day, `pe`.`birthdate`, min(`s`.`start_date`))) AS `age_in_days`
|
|
2229
|
+
from
|
|
2230
|
+
((`patient_program` `p`
|
|
2231
|
+
left join `person` `pe` ON ((`pe`.`person_id` = `p`.`patient_id`))
|
|
2232
|
+
left join `patient_state` `s` ON ((`p`.`patient_program_id` = `s`.`patient_program_id`)))
|
|
2233
|
+
left join `person` ON ((`person`.`person_id` = `p`.`patient_id`)))
|
|
2234
|
+
where
|
|
2235
|
+
((`p`.`voided` = 0)
|
|
2236
|
+
and (`s`.`voided` = 0)
|
|
2237
|
+
and (`p`.`program_id` = 1)
|
|
2238
|
+
and (`s`.`state` = 7))
|
|
2239
|
+
group by `p`.`patient_id`;
|
|
2240
|
+
SQL
|
|
2241
|
+
end
|
|
2242
|
+
|
|
2243
|
+
def arv_orders
|
|
2244
|
+
::Order.joins(:drug_order).where(
|
|
2245
|
+
'drug_order.drug_inventory_id in (?)', ::Drug.arv_drugs.collect(&:drug_id)
|
|
2246
|
+
).order(:start_date)
|
|
2247
|
+
end
|
|
2248
|
+
|
|
2249
|
+
def add_patient_record(person, order, cohort_struct)
|
|
2250
|
+
date_enrolled = order.start_date.to_date
|
|
2251
|
+
art_earliest_start_date = patient_earliest_start_date(order.patient_id, date_enrolled)
|
|
2252
|
+
|
|
2253
|
+
if date_enrolled == art_earliest_start_date
|
|
2254
|
+
cohort_struct.cum_initiated_on_art_first_time ||= 0
|
|
2255
|
+
cohort_struct.cum_initiated_on_art_first_time += 1
|
|
2256
|
+
end
|
|
2257
|
+
|
|
2258
|
+
age_in_months_when_starting = (art_earliest_start_date - person.birthdate).to_i
|
|
2259
|
+
age_when_starting = (age_in_months_when_starting / 365).to_i
|
|
2260
|
+
deathdate = person.death_date ? "'#{person.death_date.to_date}'" : 'NULL'
|
|
2261
|
+
|
|
2262
|
+
ActiveRecord::Base.connection.execute(
|
|
2263
|
+
"INSERT INTO temp_earliest_start_date (
|
|
2264
|
+
patient_id,
|
|
2265
|
+
date_enrolled,
|
|
2266
|
+
earliest_start_date,
|
|
2267
|
+
gender,
|
|
2268
|
+
birthdate,
|
|
2269
|
+
birthdate_estimated,
|
|
2270
|
+
death_date,
|
|
2271
|
+
age_at_initiation,
|
|
2272
|
+
age_in_days
|
|
2273
|
+
) VALUES (
|
|
2274
|
+
#{order.patient_id},
|
|
2275
|
+
'#{order.start_date.to_date}',
|
|
2276
|
+
'#{art_earliest_start_date.to_date}',
|
|
2277
|
+
'#{person.gender}',
|
|
2278
|
+
'#{person.birthdate}',
|
|
2279
|
+
'#{person.birthdate_estimated}',
|
|
2280
|
+
#{deathdate},
|
|
2281
|
+
'#{age_when_starting}',
|
|
2282
|
+
'#{age_in_months_when_starting}'
|
|
2283
|
+
)"
|
|
2284
|
+
)
|
|
2285
|
+
end
|
|
2286
|
+
|
|
2287
|
+
# Retrieve the earliest (clinic?) start date for a patient
|
|
2288
|
+
def patient_earliest_start_date(patient_id, min_start_date)
|
|
2289
|
+
result = ActiveRecord::Base.connection.select_one(
|
|
2290
|
+
"SELECT date_antiretrovirals_started(
|
|
2291
|
+
#{patient_id}, '#{min_start_date.to_date}'
|
|
2292
|
+
) AS date"
|
|
2293
|
+
)
|
|
2294
|
+
result['date'].to_date
|
|
2295
|
+
end
|
|
2296
|
+
|
|
2297
|
+
# Returns a list of reasons for starting ART for each patient.
|
|
2298
|
+
def patients_art_start_reason(patient_ids)
|
|
2299
|
+
ActiveRecord::Base.connection.execute(
|
|
2300
|
+
"SELECT person_id as patient_id, name, obs_datetime
|
|
2301
|
+
FROM reason_for_art_eligibility_obs
|
|
2302
|
+
WHERE #{in_manager(column: 'person_id',
|
|
2303
|
+
values: patient_ids)} #{site_manager(operator: 'AND',
|
|
2304
|
+
column: 'reason_for_art_eligibility_obs.site_id', location: @location)}"
|
|
2305
|
+
)
|
|
2306
|
+
end
|
|
2307
|
+
|
|
2308
|
+
def patient_death_date(patient)
|
|
2309
|
+
::PatientState.find_by(program: program('HIV PROGRAM'), patient:)
|
|
2310
|
+
end
|
|
2311
|
+
|
|
2312
|
+
# Filter out patients with given start causes from patient_ids
|
|
2313
|
+
def filter_patients_with_start_cause(patient_ids, start_cause_concept_ids)
|
|
2314
|
+
obs_concepts = start_cause_concept_ids.push(concept('WHO STAGES CRITERIA PRESENT').concept_id)
|
|
2315
|
+
obs_values = start_cause_concept_ids.push(concept('YES').concept_id)
|
|
2316
|
+
|
|
2317
|
+
ActiveRecord::Base.connection.execute(
|
|
2318
|
+
"SELECT *
|
|
2319
|
+
FROM hiv_staging_conditions_obs
|
|
2320
|
+
WHERE #{in_manager(column: 'concept_id', values: obs_concepts)}
|
|
2321
|
+
AND #{in_manager(column: 'value_coded', values: obs_values)}
|
|
2322
|
+
AND #{in_manager(column: 'person_id',
|
|
2323
|
+
values: patient_ids)} #{site_manager(operator: 'AND',
|
|
2324
|
+
column: 'hiv_staging_conditions_obs.site_id', location: @location)}
|
|
2325
|
+
GROUP BY person_id"
|
|
2326
|
+
)
|
|
2327
|
+
end
|
|
2328
|
+
|
|
2329
|
+
def patient_in_program?(patient)
|
|
2330
|
+
return false unless patient
|
|
2331
|
+
|
|
2332
|
+
pprogram = ::PatientProgram.find_by(program: program('HIV PROGRAM'), patient:)
|
|
2333
|
+
return false unless pprogram
|
|
2334
|
+
|
|
2335
|
+
::PatientState.where(patient_program: pprogram, state: 7).exists?
|
|
2336
|
+
end
|
|
2337
|
+
end
|
|
2338
|
+
end
|
|
2339
|
+
end
|
|
2340
|
+
# rubocop:enable Metrics/ClassLength
|