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