malawi_hiv_program_reports 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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,456 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MalawiHivProgramReports
|
4
|
+
module Clinic
|
5
|
+
class RegimenSwitch
|
6
|
+
def initialize(start_date:, end_date:, **kwargs)
|
7
|
+
@start_date = start_date
|
8
|
+
@end_date = end_date
|
9
|
+
@occupation = kwargs[:occupation]
|
10
|
+
end
|
11
|
+
|
12
|
+
def regimen_switch(pepfar)
|
13
|
+
swicth_report(pepfar)
|
14
|
+
end
|
15
|
+
|
16
|
+
def regimen_report(type)
|
17
|
+
RegimenDispensationData.new(type:, start_date: @start_date,
|
18
|
+
end_date: @end_date, occupation: @occupation)
|
19
|
+
.find_report
|
20
|
+
end
|
21
|
+
|
22
|
+
def latest_regimen_dispensed(rebuild_outcome)
|
23
|
+
if rebuild_outcome || @occupation.present?
|
24
|
+
MalawiHivProgramReports::Moh::CohortBuilder.new(outcomes_definition: 'moh')
|
25
|
+
.init_temporary_tables(@start_date, @end_date, @occupation)
|
26
|
+
end
|
27
|
+
|
28
|
+
latest_regimens
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def latest_regimens
|
34
|
+
pills_dispensed = ::ConceptName.find_by_name('Amount of drug dispensed').concept_id
|
35
|
+
patient_identifier_type = ::PatientIdentifierType.find_by_name('ARV Number').id
|
36
|
+
|
37
|
+
arv_dispensentions = ActiveRecord::Base.connection.select_all <<~SQL
|
38
|
+
SELECT
|
39
|
+
o.patient_id, drug.drug_id, o.order_id, i.identifier,
|
40
|
+
drug.name, d.quantity, o.start_date, obs.value_numeric,
|
41
|
+
person.birthdate, person.gender
|
42
|
+
FROM orders o
|
43
|
+
INNER JOIN drug_order d ON d.order_id = o.order_id AND d.quantity > 0
|
44
|
+
INNER JOIN drug ON drug.drug_id = d.drug_inventory_id
|
45
|
+
INNER JOIN arv_drug On arv_drug.drug_id = drug.drug_id
|
46
|
+
INNER JOIN temp_patient_outcomes t ON o.patient_id = t.patient_id AND t.cum_outcome = 'On antiretrovirals'
|
47
|
+
INNER JOIN person ON person.person_id = o.patient_id AND person.voided = 0
|
48
|
+
INNER JOIN (
|
49
|
+
SELECT MAX(o.start_date) start_date, o.patient_id
|
50
|
+
FROM orders o
|
51
|
+
INNER JOIN drug_order dor ON dor.order_id = o.order_id AND dor.quantity > 0
|
52
|
+
AND dor.drug_inventory_id IN (SELECT drug_id FROM arv_drug)
|
53
|
+
WHERE o.voided = 0
|
54
|
+
AND o.start_date <= '#{@end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
|
55
|
+
AND o.start_date >= '#{@start_date.to_date.strftime('%Y-%m-%d 00:00:00')}'
|
56
|
+
GROUP BY o.patient_id
|
57
|
+
) lor ON lor.start_date = o.start_date AND lor.patient_id = o.patient_id
|
58
|
+
LEFT JOIN obs on obs.order_id = o.order_id AND obs.concept_id=#{pills_dispensed} AND obs.voided = 0
|
59
|
+
LEFT JOIN patient_identifier i ON i.patient_id = o.patient_id
|
60
|
+
AND i.identifier_type = #{patient_identifier_type} AND i.voided = 0
|
61
|
+
WHERE o.voided = 0
|
62
|
+
AND o.start_date <= '#{@end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
|
63
|
+
AND o.start_date >= '#{@start_date.to_date.strftime('%Y-%m-%d 00:00:00')}'
|
64
|
+
ORDER BY o.patient_id
|
65
|
+
SQL
|
66
|
+
|
67
|
+
patient_list = arv_dispensentions.map { |d| d['patient_id'] }.uniq.push(0)
|
68
|
+
|
69
|
+
@latest_vl = latest_vl_orders(patient_list)
|
70
|
+
@latest_result = latest_vl_results(patient_list)
|
71
|
+
|
72
|
+
formated_data = {}
|
73
|
+
|
74
|
+
(arv_dispensentions || []).each do |data|
|
75
|
+
dispensation_date = data['start_date'].to_date
|
76
|
+
patient_id = data['patient_id'].to_i
|
77
|
+
order_id = data['order_id'].to_i
|
78
|
+
# drug_id = data['drug_id'].to_i
|
79
|
+
medication = data['name']
|
80
|
+
quantity = data['quantity'].to_f
|
81
|
+
value_numeric = data['value_numeric'].to_f
|
82
|
+
drug_id = data['drug_id'].to_i
|
83
|
+
# find the latest vl result for the patient from the array of vl results {patient_id: number, order_date: date}
|
84
|
+
latest_vl = @latest_vl.select { |vl| vl['patient_id'] == patient_id }&.first
|
85
|
+
latest_result = @latest_result.select { |vl| vl['patient_id'] == patient_id }&.first
|
86
|
+
|
87
|
+
formated_data[patient_id] = {} if formated_data[patient_id].blank?
|
88
|
+
if formated_data[patient_id][order_id].blank?
|
89
|
+
formated_data[patient_id][order_id] = {
|
90
|
+
name: medication,
|
91
|
+
quantity:,
|
92
|
+
dispensation_date:,
|
93
|
+
identifier: data['identifier'],
|
94
|
+
gender: data['gender'],
|
95
|
+
birthdate: data['birthdate'],
|
96
|
+
drug_id:,
|
97
|
+
pack_sizes: [],
|
98
|
+
vl_latest_order_date: latest_vl.present? ? latest_vl['order_date']&.to_date : 'N/A',
|
99
|
+
vl_latest_result_date: latest_result.present? ? latest_result['result_date']&.to_date : 'N/A',
|
100
|
+
vl_latest_result: latest_result.present? ? latest_result['result'] : 'N/A'
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
formated_data[patient_id][order_id][:pack_sizes] << value_numeric
|
105
|
+
end
|
106
|
+
|
107
|
+
formated_data
|
108
|
+
end
|
109
|
+
|
110
|
+
def regimen_data
|
111
|
+
::EncounterType.find_by_name('DISPENSING').id
|
112
|
+
arv_concept_id = ::ConceptName.find_by_name('Antiretroviral drugs').concept_id
|
113
|
+
|
114
|
+
drug_ids = ::Drug.joins('INNER JOIN concept_set s ON s.concept_id = drug.concept_id')
|
115
|
+
.where('s.concept_set = ?', arv_concept_id).map(&:drug_id)
|
116
|
+
|
117
|
+
ActiveRecord::Base.connection.execute('drop table if exists tmp_latest_arv_dispensation ;')
|
118
|
+
|
119
|
+
ActiveRecord::Base.connection.execute("
|
120
|
+
create table tmp_latest_arv_dispensation
|
121
|
+
SELECT patient_id,DATE(MAX(start_date)) as start_date
|
122
|
+
FROM orders INNER JOIN drug_order t USING (order_id)
|
123
|
+
WHERE
|
124
|
+
(
|
125
|
+
start_date BETWEEN '#{@start_date.to_date.strftime('%Y-%m-%d 00:00:00')}' AND '#{@end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
|
126
|
+
AND t.drug_inventory_id IN (#{drug_ids.join(',')}) AND t.quantity > 0
|
127
|
+
)
|
128
|
+
group by patient_id")
|
129
|
+
|
130
|
+
ActiveRecord::Base.connection.execute('create index lad_patient_id_and_start_date on tmp_latest_arv_dispensation (start_date, patient_id);')
|
131
|
+
|
132
|
+
arv_dispensentions = ActiveRecord::Base.connection.select_all <<~SQL
|
133
|
+
SELECT
|
134
|
+
o.patient_id patient_id, o.start_date, o.order_id,
|
135
|
+
d.quantity, drug.name
|
136
|
+
FROM orders o
|
137
|
+
INNER JOIN drug_order d ON o.order_id = d.order_id
|
138
|
+
INNER JOIN drug ON d.drug_inventory_id = drug.drug_id
|
139
|
+
INNER JOIN tmp_latest_arv_dispensation k on (o.patient_id = k.patient_id and DATE(o.start_date) = k.start_date)
|
140
|
+
WHERE d.drug_inventory_id IN(#{drug_ids.join(',')})
|
141
|
+
AND d.quantity > 0 AND o.voided = 0 AND o.start_date BETWEEN '#{@start_date.to_date.strftime('%Y-%m-%d 00:00:00')}'
|
142
|
+
AND '#{@end_date.to_date.strftime('%Y-%m-%d 23:59:59')}' GROUP BY o.order_id;
|
143
|
+
SQL
|
144
|
+
|
145
|
+
patient_ids = []
|
146
|
+
(arv_dispensentions || []).each do |data|
|
147
|
+
patient_ids << data['patient_id'].to_i
|
148
|
+
end
|
149
|
+
return [] if patient_ids.blank?
|
150
|
+
|
151
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
152
|
+
SELECT
|
153
|
+
`p`.`patient_id` AS `patient_id`,
|
154
|
+
cast(patient_date_enrolled(`p`.`patient_id`) as date) AS `date_enrolled`,
|
155
|
+
date_antiretrovirals_started(`p`.`patient_id`, min(`s`.`start_date`)) AS `earliest_start_date`
|
156
|
+
FROM
|
157
|
+
((`patient_program` `p`
|
158
|
+
LEFT JOIN `person` `pe` ON ((`pe`.`person_id` = `p`.`patient_id`))
|
159
|
+
LEFT JOIN `patient_state` `s` ON ((`p`.`patient_program_id` = `s`.`patient_program_id`)))
|
160
|
+
LEFT JOIN `person` ON ((`person`.`person_id` = `p`.`patient_id`)))
|
161
|
+
WHERE
|
162
|
+
((`p`.`voided` = 0)
|
163
|
+
AND (`s`.`voided` = 0)
|
164
|
+
AND (`p`.`program_id` = 1)
|
165
|
+
AND (`s`.`state` = 7))
|
166
|
+
AND (`s`.`start_date` <= '#{@end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
|
167
|
+
AND p.patient_id IN(#{patient_ids.join(',')}))
|
168
|
+
GROUP BY `p`.`patient_id`;
|
169
|
+
SQL
|
170
|
+
end
|
171
|
+
|
172
|
+
def arv_dispensention_data(patient_id)
|
173
|
+
::EncounterType.find_by_name('DISPENSING').id
|
174
|
+
arv_concept_id = ::ConceptName.find_by_name('Antiretroviral drugs').concept_id
|
175
|
+
|
176
|
+
drug_ids = ::Drug.joins('INNER JOIN concept_set s ON s.concept_id = drug.concept_id')
|
177
|
+
.where('s.concept_set = ?', arv_concept_id).map(&:drug_id)
|
178
|
+
|
179
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
180
|
+
SELECT
|
181
|
+
o.patient_id, drug.name, d.quantity, o.start_date
|
182
|
+
FROM orders o
|
183
|
+
INNER JOIN drug_order d ON d.order_id = o.order_id
|
184
|
+
INNER JOIN drug ON drug.drug_id = d.drug_inventory_id
|
185
|
+
WHERE d.drug_inventory_id IN(#{drug_ids.join(',')})
|
186
|
+
AND o.patient_id = #{patient_id} AND
|
187
|
+
d.quantity > 0 AND o.voided = 0 AND DATE(o.start_date) = (
|
188
|
+
SELECT DATE(MAX(start_date)) FROM orders
|
189
|
+
INNER JOIN drug_order t USING(order_id)
|
190
|
+
WHERE patient_id = o.patient_id
|
191
|
+
AND (
|
192
|
+
start_date BETWEEN '#{@start_date.to_date.strftime('%Y-%m-%d 00:00:00')}'
|
193
|
+
AND '#{@end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
|
194
|
+
AND t.drug_inventory_id IN(#{drug_ids.join(',')}) AND quantity > 0
|
195
|
+
)
|
196
|
+
) GROUP BY (o.order_id)
|
197
|
+
SQL
|
198
|
+
end
|
199
|
+
|
200
|
+
def current_regimen(type)
|
201
|
+
data = regimen_data
|
202
|
+
|
203
|
+
clients = {}
|
204
|
+
(data || []).each do |r|
|
205
|
+
patient_id = r['patient_id'].to_i
|
206
|
+
|
207
|
+
outcome_status = if type == 'pepfar'
|
208
|
+
ActiveRecord::Base.connection.select_one <<~SQL
|
209
|
+
SELECT pepfar_patient_outcome(#{patient_id}, '#{@end_date.to_date}') outcome;
|
210
|
+
SQL
|
211
|
+
|
212
|
+
else
|
213
|
+
ActiveRecord::Base.connection.select_one <<~SQL
|
214
|
+
SELECT patient_outcome(#{patient_id}, '#{@end_date.to_date}') outcome;
|
215
|
+
SQL
|
216
|
+
|
217
|
+
end
|
218
|
+
next unless outcome_status['outcome'] == 'On antiretrovirals'
|
219
|
+
|
220
|
+
medications = arv_dispensention_data(patient_id)
|
221
|
+
|
222
|
+
begin
|
223
|
+
visit_date = medications.first['start_date'].to_date
|
224
|
+
rescue StandardError
|
225
|
+
next
|
226
|
+
end
|
227
|
+
|
228
|
+
curr_reg = ActiveRecord::Base.connection.select_one <<~SQL
|
229
|
+
SELECT patient_current_regimen(#{patient_id}, '#{@end_date.to_date}') current_regimen
|
230
|
+
SQL
|
231
|
+
|
232
|
+
next unless visit_date >= @start_date.to_date && visit_date <= @end_date.to_date
|
233
|
+
|
234
|
+
if clients[patient_id].blank?
|
235
|
+
demo = ActiveRecord::Base.connection.select_one <<~SQL
|
236
|
+
SELECT
|
237
|
+
p.birthdate, p.gender, i.identifier arv_number,
|
238
|
+
n.given_name, n.family_name
|
239
|
+
FROM person p
|
240
|
+
LEFT JOIN person_name n ON n.person_id = p.person_id AND n.voided = 0
|
241
|
+
LEFT JOIN patient_identifier i ON i.patient_id = p.person_id
|
242
|
+
AND i.identifier_type = 4 AND i.voided = 0
|
243
|
+
WHERE p.person_id = #{patient_id} GROUP BY p.person_id
|
244
|
+
ORDER BY n.date_created DESC, i.date_created DESC;
|
245
|
+
SQL
|
246
|
+
|
247
|
+
viral_load = latest_vl_results([patient_id])
|
248
|
+
clients[patient_id] = {
|
249
|
+
arv_number: demo['arv_number'],
|
250
|
+
given_name: demo['given_name'],
|
251
|
+
family_name: demo['family_name'],
|
252
|
+
birthdate: demo['birthdate'],
|
253
|
+
gender: demo['gender'] == 'M' ? 'M' : maternal_status(patient_id, demo['gender']),
|
254
|
+
current_regimen: curr_reg['current_regimen'],
|
255
|
+
current_weight: current_weight(patient_id),
|
256
|
+
art_start_date: r['earliest_start_date'],
|
257
|
+
medication: [],
|
258
|
+
vl_result: viral_load ? viral_load['result'] : nil,
|
259
|
+
vl_result_date: viral_load ? viral_load['result_date'] : nil
|
260
|
+
}
|
261
|
+
end
|
262
|
+
|
263
|
+
(medications || []).each do |med|
|
264
|
+
clients[patient_id][:medication] << {
|
265
|
+
medication: med['name'],
|
266
|
+
quantity: med['quantity'],
|
267
|
+
start_date: visit_date
|
268
|
+
}
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
clients
|
273
|
+
end
|
274
|
+
|
275
|
+
def swicth_report(pepfar)
|
276
|
+
clients = {}
|
277
|
+
data = regimen_data
|
278
|
+
pepfar_outcome_builder(pepfar.blank? ? 'moh' : 'pepfar')
|
279
|
+
|
280
|
+
(data || []).each do |r|
|
281
|
+
patient_id = r['patient_id'].to_i
|
282
|
+
medications = arv_dispensention_data(patient_id)
|
283
|
+
|
284
|
+
outcome_status = ActiveRecord::Base.connection.select_one <<~SQL
|
285
|
+
SELECT cum_outcome FROM temp_patient_outcomes WHERE patient_id = #{patient_id};
|
286
|
+
SQL
|
287
|
+
|
288
|
+
next if outcome_status.blank?
|
289
|
+
next if outcome_status['cum_outcome'].blank?
|
290
|
+
next unless outcome_status['cum_outcome'] == 'On antiretrovirals'
|
291
|
+
|
292
|
+
visit_date = medications.first['start_date']
|
293
|
+
visit_date.blank? ? next : (visit_date = visit_date.to_date)
|
294
|
+
|
295
|
+
next unless visit_date >= @start_date.to_date && visit_date <= @end_date.to_date
|
296
|
+
|
297
|
+
prev_reg = ActiveRecord::Base.connection.select_one <<~SQL
|
298
|
+
SELECT patient_current_regimen(#{patient_id}, '#{(visit_date - 1.day).to_date}') previous_regimen
|
299
|
+
SQL
|
300
|
+
|
301
|
+
current_reg = ActiveRecord::Base.connection.select_one <<~SQL
|
302
|
+
SELECT patient_current_regimen(#{patient_id}, '#{visit_date}') current_regimen
|
303
|
+
SQL
|
304
|
+
|
305
|
+
next if prev_reg['previous_regimen'] == current_reg['current_regimen']
|
306
|
+
next if prev_reg['previous_regimen'] == 'N/A'
|
307
|
+
|
308
|
+
if clients[patient_id].blank?
|
309
|
+
demo = ActiveRecord::Base.connection.select_one <<~SQL
|
310
|
+
SELECT
|
311
|
+
p.birthdate, p.gender, i.identifier arv_number,
|
312
|
+
n.given_name, n.family_name, p.person_id
|
313
|
+
FROM person p
|
314
|
+
LEFT JOIN person_name n ON n.person_id = p.person_id AND n.voided = 0
|
315
|
+
LEFT JOIN patient_identifier i ON i.patient_id = p.person_id
|
316
|
+
AND i.identifier_type = 4 AND i.voided = 0
|
317
|
+
WHERE p.person_id = #{patient_id} GROUP BY p.person_id
|
318
|
+
ORDER BY n.date_created DESC, i.date_created DESC
|
319
|
+
SQL
|
320
|
+
|
321
|
+
clients[patient_id] = {
|
322
|
+
arv_number: (demo['arv_number'].blank? ? 'N/A' : demo['arv_number']),
|
323
|
+
given_name: demo['given_name'],
|
324
|
+
family_name: demo['family_name'],
|
325
|
+
birthdate: demo['birthdate'],
|
326
|
+
gender: demo['gender'],
|
327
|
+
previous_regimen: prev_reg['previous_regimen'],
|
328
|
+
current_regimen: current_reg['current_regimen'],
|
329
|
+
patient_type: get_patient_type(demo['person_id'], pepfar),
|
330
|
+
current_weight: current_weight(demo['person_id']),
|
331
|
+
art_start_date: r['earliest_start_date'],
|
332
|
+
medication: []
|
333
|
+
}
|
334
|
+
end
|
335
|
+
|
336
|
+
(medications || []).each do |m|
|
337
|
+
clients[patient_id][:medication] << {
|
338
|
+
medication: m['name'], quantity: m['quantity'],
|
339
|
+
start_date: visit_date
|
340
|
+
}
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
clients
|
345
|
+
end
|
346
|
+
|
347
|
+
def get_patient_type(patient_id, pepfar)
|
348
|
+
return nil unless pepfar
|
349
|
+
|
350
|
+
concept_id = ::ConceptName.find_by_name('Type of patient').concept_id
|
351
|
+
ext_id = ::ConceptName.find_by_name('External consultation').concept_id
|
352
|
+
obs = ::Observation.where(concept_id:, value_coded: ext_id, person_id: patient_id)
|
353
|
+
(obs.blank? ? 'Resident' : 'External')
|
354
|
+
end
|
355
|
+
|
356
|
+
def pepfar_outcome_builder(repport_type = 'moh')
|
357
|
+
cohort_builder = MalawiHivProgramReports::Moh::CohortDisaggregated.new(name: 'Regimen switch', type: repport_type,
|
358
|
+
start_date: @start_date.to_date,
|
359
|
+
end_date: @end_date.to_date, rebuild: true,
|
360
|
+
occupation: @occupation)
|
361
|
+
cohort_builder.rebuild_outcomes(repport_type)
|
362
|
+
end
|
363
|
+
|
364
|
+
def current_weight(patient_id)
|
365
|
+
weight_concept = ::ConceptName.find_by_name('Weight (kg)').concept_id
|
366
|
+
obs = ::Observation.where("person_id = ? AND concept_id = ?
|
367
|
+
AND obs_datetime <= ? AND (value_numeric IS NOT NULL OR value_text IS NOT NULL)",
|
368
|
+
patient_id, weight_concept, @end_date.to_date.strftime('%Y-%m-%d 23:59:59'))
|
369
|
+
.order('obs_datetime DESC, date_created DESC')
|
370
|
+
|
371
|
+
return nil if obs.blank?
|
372
|
+
|
373
|
+
(obs.first.value_numeric.blank? ? obs.first.value_text : obs.first.value_numeric)
|
374
|
+
end
|
375
|
+
|
376
|
+
# def vl_result(patient_id)
|
377
|
+
# ActiveRecord::Base.connection.select_one <<~SQL
|
378
|
+
# SELECT lab_result_obs.obs_datetime AS result_date,
|
379
|
+
# CONCAT (COALESCE(measure.value_modifier, '='),' ',COALESCE(measure.value_numeric, measure.value_text, '')) as result
|
380
|
+
# FROM obs AS lab_result_obs
|
381
|
+
# INNER JOIN orders
|
382
|
+
# ON orders.order_id = lab_result_obs.order_id
|
383
|
+
# AND orders.voided = 0
|
384
|
+
# INNER JOIN obs AS measure
|
385
|
+
# ON measure.obs_group_id = lab_result_obs.obs_id
|
386
|
+
# AND measure.voided = 0
|
387
|
+
# INNER JOIN (
|
388
|
+
# SELECT concept_id, name
|
389
|
+
# FROM concept_name
|
390
|
+
# INNER JOIN concept USING (concept_id)
|
391
|
+
# WHERE concept.retired = 0
|
392
|
+
# AND name NOT LIKE 'Lab test result'
|
393
|
+
# GROUP BY concept_id
|
394
|
+
# ) AS measure_concept
|
395
|
+
# ON measure_concept.concept_id = measure.concept_id
|
396
|
+
# WHERE lab_result_obs.voided = 0
|
397
|
+
# AND measure.person_id = #{patient_id}
|
398
|
+
# AND (measure.value_numeric IS NOT NULL || measure.value_text IS NOT NULL)
|
399
|
+
# AND lab_result_obs.obs_datetime <= '#{@end_date.to_date.strftime('%Y-%m-%d 23:59:59')}'
|
400
|
+
# ORDER BY lab_result_obs.obs_datetime DESC
|
401
|
+
# LIMIT 1
|
402
|
+
# SQL
|
403
|
+
# end
|
404
|
+
|
405
|
+
def latest_vl_orders(patient_list)
|
406
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
407
|
+
SELECT odr.patient_id, MAX(start_date) AS order_date
|
408
|
+
FROM obs o
|
409
|
+
INNER JOIN orders odr ON odr.order_id = o.order_id AND odr.voided = 0 AND DATE(odr.start_date) <= '#{@end_date}'
|
410
|
+
WHERE o.concept_id = #{::ConceptName.find_by_name('Test Type').concept_id}
|
411
|
+
AND o.value_coded = #{::ConceptName.find_by_name('HIV viral load').concept_id}
|
412
|
+
AND o.voided = 0
|
413
|
+
AND o.person_id IN (#{patient_list.join(',')})
|
414
|
+
GROUP BY odr.patient_id
|
415
|
+
SQL
|
416
|
+
end
|
417
|
+
|
418
|
+
def latest_vl_results(patient_list)
|
419
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
420
|
+
SELECT o.person_id AS patient_id,
|
421
|
+
o.obs_datetime AS result_date,
|
422
|
+
CONCAT (COALESCE(o.value_modifier, '='),' ',COALESCE(o.value_numeric, o.value_text, '')) AS result
|
423
|
+
FROM obs o
|
424
|
+
INNER JOIN (
|
425
|
+
SELECT MAX(obs_datetime) AS obs_datetime, person_id
|
426
|
+
FROM obs co
|
427
|
+
INNER JOIN orders odr ON odr.order_id = co.order_id AND odr.voided = 0
|
428
|
+
WHERE co.concept_id = #{::ConceptName.find_by_name('HIV viral load').concept_id}
|
429
|
+
AND co.voided = 0
|
430
|
+
AND co.obs_datetime <= '#{@end_date}'
|
431
|
+
AND (co.value_numeric IS NOT NULL || co.value_text IS NOT NULL)
|
432
|
+
AND co.person_id IN (#{patient_list.join(',')})
|
433
|
+
GROUP BY co.person_id
|
434
|
+
) AS latest_vl ON latest_vl.obs_datetime = o.obs_datetime AND latest_vl.person_id = o.person_id
|
435
|
+
INNER JOIN orders odr ON odr.order_id = o.order_id AND odr.voided = 0
|
436
|
+
WHERE o.concept_id = #{::ConceptName.find_by_name('HIV viral load').concept_id}
|
437
|
+
AND o.voided = 0 AND o.obs_datetime <= '#{@end_date}'
|
438
|
+
AND (o.value_numeric IS NOT NULL || o.value_text IS NOT NULL)
|
439
|
+
AND o.person_id IN (#{patient_list.join(',')})
|
440
|
+
ORDER BY o.obs_datetime DESC
|
441
|
+
SQL
|
442
|
+
end
|
443
|
+
|
444
|
+
def maternal_status(patient_id, current_gender)
|
445
|
+
return nil if current_gender.blank?
|
446
|
+
|
447
|
+
result = MalawiHivProgramReports::Pepfar::ViralLoadCoverage2.new(start_date: @start_date,
|
448
|
+
end_date: @end_date).vl_maternal_status([patient_id])
|
449
|
+
gender = 'FNP'
|
450
|
+
gender = 'FP' unless result[:FP].blank?
|
451
|
+
gender = 'FBf' unless result[:FBf].blank?
|
452
|
+
gender
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Retrieve patients in a particular regimen and formulation.
|
4
|
+
module MalawiHivProgramReports
|
5
|
+
module Clinic
|
6
|
+
class RegimensAndFormulations
|
7
|
+
include MalawiHivProgramReports::Utils::CommonSqlQueryUtils
|
8
|
+
|
9
|
+
attr_reader :start_date, :end_date, :regimen, :formulation
|
10
|
+
|
11
|
+
def initialize(start_date:, end_date:, regimen: nil, formulation: 'tablets', **kwargs)
|
12
|
+
raise ::InvalidParameterError, 'regimen is required' unless regimen
|
13
|
+
|
14
|
+
unless %w[granules tablets pellets].include?(formulation)
|
15
|
+
raise ::InvalidParameterError, "Invalid formalation: #{formulation}"
|
16
|
+
end
|
17
|
+
|
18
|
+
@start_date = start_date.to_date
|
19
|
+
@end_date = end_date.to_date
|
20
|
+
@formulation = formulation
|
21
|
+
@regimen = regimen
|
22
|
+
@occupation = kwargs[:occupation]
|
23
|
+
end
|
24
|
+
|
25
|
+
def find_report
|
26
|
+
patients
|
27
|
+
end
|
28
|
+
|
29
|
+
def patients
|
30
|
+
patients_with_prescriptions.each_with_object([]) do |patient, matching_patients|
|
31
|
+
prescribed_drugs = drugs_prescribed_to_patient(patient.patient_id, patient.prescription_date).map(&:drug_id)
|
32
|
+
non_matching_drugs = Set.new(drugs) - prescribed_drugs
|
33
|
+
|
34
|
+
next unless non_matching_drugs.empty?
|
35
|
+
|
36
|
+
demographics = patient_demographics(patient.patient_id, patient.prescription_date)
|
37
|
+
|
38
|
+
matching_patients << {
|
39
|
+
patient_id: demographics.patient_id,
|
40
|
+
arv_number: demographics.arv_number,
|
41
|
+
birthdate: demographics.birthdate,
|
42
|
+
gender: demographics.gender,
|
43
|
+
weight: demographics.weight
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
TABLET_REGIMENS = {
|
51
|
+
'0A' => [969, 22],
|
52
|
+
'0P' => [1044, 968],
|
53
|
+
'2A' => [731],
|
54
|
+
'2P' => [732],
|
55
|
+
'4A' => [39, 11],
|
56
|
+
'4P' => [736, 30],
|
57
|
+
'5A' => [735],
|
58
|
+
'6A' => [734, 22],
|
59
|
+
'7A' => [734, 932],
|
60
|
+
'8A' => [39, 932],
|
61
|
+
'9A' => [969, 73],
|
62
|
+
'9P' => [1044, 74],
|
63
|
+
'10A' => [734, 73],
|
64
|
+
'11A' => [39, 73],
|
65
|
+
'11P' => [736, 74],
|
66
|
+
'12A' => [982, 977, 976],
|
67
|
+
'13A' => [983],
|
68
|
+
'14A' => [984, 982],
|
69
|
+
'14P' => [736, 982],
|
70
|
+
'15A' => [969, 982],
|
71
|
+
'15P' => [1044, 982],
|
72
|
+
'16A' => [969, 954],
|
73
|
+
'16P' => [1044, 1043],
|
74
|
+
'17A' => [969, 11],
|
75
|
+
'17P' => [1044, 30]
|
76
|
+
}.freeze
|
77
|
+
|
78
|
+
GRANULES_REGIMENS = {
|
79
|
+
'9P' => [1044, 1045],
|
80
|
+
'11P' => [736, 1045]
|
81
|
+
}.freeze
|
82
|
+
|
83
|
+
PELLETS_REGIMENS = {
|
84
|
+
'9P' => [1044, 979],
|
85
|
+
'11P' => [736, 979]
|
86
|
+
}.freeze
|
87
|
+
|
88
|
+
REGIMENS_BY_FORMULATION = {
|
89
|
+
'granules' => GRANULES_REGIMENS,
|
90
|
+
'pellets' => PELLETS_REGIMENS,
|
91
|
+
'tablets' => TABLET_REGIMENS
|
92
|
+
}.freeze
|
93
|
+
|
94
|
+
# Returns drugs in selected regimen and formulation
|
95
|
+
def drugs
|
96
|
+
REGIMENS_BY_FORMULATION[formulation][regimen]
|
97
|
+
end
|
98
|
+
|
99
|
+
def patients_with_prescriptions
|
100
|
+
return [] if drugs.nil?
|
101
|
+
|
102
|
+
::DrugOrder.select('orders.patient_id AS patient_id, MAX(start_date) AS prescription_date')
|
103
|
+
.joins(:order)
|
104
|
+
.joins("LEFT JOIN (#{current_occupation_query}) AS a ON a.person_id = orders.patient_id")
|
105
|
+
.where(quantity: 1..Float::INFINITY, drug_inventory_id: drugs)
|
106
|
+
.where(occupation_filter(occupation: @occupation, field_name: 'value', table_name: 'a',
|
107
|
+
include_clause: false).to_s)
|
108
|
+
.merge(treatment_orders)
|
109
|
+
.group('orders.patient_id')
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns all orders in treatment encounter of HIV PROGRAM
|
113
|
+
def treatment_orders
|
114
|
+
::Order.joins(:encounter)
|
115
|
+
.where(start_date: start_date..end_date)
|
116
|
+
.merge(treatment_encounter)
|
117
|
+
.or(::Order.joins(:encounter)
|
118
|
+
.where(auto_expire_date: start_date..end_date)
|
119
|
+
.merge(treatment_encounter))
|
120
|
+
.or(::Order.joins(:encounter)
|
121
|
+
.where('start_date < ? AND auto_expire_date > ?', start_date, end_date)
|
122
|
+
.merge(treatment_encounter))
|
123
|
+
end
|
124
|
+
|
125
|
+
def treatment_encounter
|
126
|
+
::Encounter.where(encounter_type: ::EncounterType.find_by_name('Treatment'),
|
127
|
+
program_id: Constants::PROGRAM_ID)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns drugs prescribed to patient on given day
|
131
|
+
def drugs_prescribed_to_patient(patient_id, prescription_date)
|
132
|
+
::DrugOrder.select('drug_order.drug_inventory_id AS drug_id')
|
133
|
+
.joins(:order)
|
134
|
+
.where(quantity: 1..Float::INFINITY, drug_inventory_id: drugs)
|
135
|
+
.merge(::Order.joins(:encounter)
|
136
|
+
.where(patient_id:, start_date: prescription_date)
|
137
|
+
.merge(treatment_encounter))
|
138
|
+
end
|
139
|
+
|
140
|
+
def patient_demographics(patient_id, prescription_date)
|
141
|
+
::Person.find_by_sql(
|
142
|
+
<<~SQL
|
143
|
+
SELECT person.person_id AS patient_id, patient_identifier.identifier AS arv_number,
|
144
|
+
person.birthdate AS birthdate, person.gender AS gender, obs.value_numeric AS weight
|
145
|
+
FROM person
|
146
|
+
LEFT JOIN patient_identifier ON patient_identifier.patient_id = person.person_id
|
147
|
+
AND patient_identifier.identifier_type = #{arv_number_type_id}
|
148
|
+
AND patient_identifier.voided = 0
|
149
|
+
LEFT JOIN obs ON obs.person_id = person.person_id
|
150
|
+
AND obs.concept_id = #{weight_concept_id}
|
151
|
+
AND obs.obs_datetime = (
|
152
|
+
SELECT MAX(obs_datetime) FROM obs
|
153
|
+
WHERE person_id = #{patient_id}
|
154
|
+
AND concept_id = #{weight_concept_id}
|
155
|
+
AND obs_datetime <= '#{prescription_date}'
|
156
|
+
AND voided = 0
|
157
|
+
) AND obs.voided = 0
|
158
|
+
WHERE person.person_id = #{patient_id}
|
159
|
+
SQL
|
160
|
+
).first
|
161
|
+
end
|
162
|
+
|
163
|
+
def patient_recent_weight(patient_id, as_of)
|
164
|
+
::Observation.select(:value_numeric)
|
165
|
+
.where(concept_id: ::ConceptName.find_by_name('Weight (kg)').concept_id,
|
166
|
+
person_id: patient_id)
|
167
|
+
.where('obs_datetime < ? AND value_numeric IS NOT NULL', as_of)
|
168
|
+
.order(obs_datetime: :desc)
|
169
|
+
.first
|
170
|
+
&.value_numeric
|
171
|
+
end
|
172
|
+
|
173
|
+
def weight_concept_id
|
174
|
+
@weight_concept_id ||= ::ConceptName.find_by_name('Weight (kg)').concept_id
|
175
|
+
end
|
176
|
+
|
177
|
+
def arv_number_type_id
|
178
|
+
@arv_number_type_id ||= ::PatientIdentifierType.find_by_name('ARV Number').id
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|