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,433 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MalawiHivProgramReports
|
4
|
+
module Pepfar
|
5
|
+
## Viral Load Coverage Report
|
6
|
+
# 1. given the start and end dates, this report will go back 12 months using the end date
|
7
|
+
# 2. pick all clients that are due in the mentioned period
|
8
|
+
# 3. the picked clients should also include those that are new on ART 6 months before the end date
|
9
|
+
# 4. for the sample drawns available pick the latest sample drawn within the reporting period
|
10
|
+
# 5. for the results pick the latest result within the reporting period
|
11
|
+
class ViralLoadCoverage2
|
12
|
+
|
13
|
+
include Utils
|
14
|
+
include MalawiHivProgramReports::Utils::CommonSqlQueryUtils
|
15
|
+
include MalawiHivProgramReports::Adapters::Moh::Custom
|
16
|
+
include MalawiHivProgramReports::Utils::ModelUtils
|
17
|
+
attr_reader :start_date, :end_date, :location
|
18
|
+
|
19
|
+
def initialize(start_date:, end_date:, **kwargs)
|
20
|
+
@start_date = start_date&.to_date
|
21
|
+
raise InvalidParameterError, 'start_date is required' unless @start_date
|
22
|
+
|
23
|
+
@end_date = end_date&.to_date || @start_date + 12.months
|
24
|
+
raise InvalidParameterError, "start_date can't be greater than end_date" if @start_date > @end_date
|
25
|
+
|
26
|
+
@occupation = kwargs.delete(:occupation)
|
27
|
+
@type = kwargs.delete(:application)
|
28
|
+
@location = kwargs.delete(:location)
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_report
|
32
|
+
report = init_report
|
33
|
+
build_report(report)
|
34
|
+
report
|
35
|
+
end
|
36
|
+
|
37
|
+
def vl_maternal_status(patient_list)
|
38
|
+
return { FP: [], FBf: [] } if patient_list.blank?
|
39
|
+
|
40
|
+
pregnant = pregnant_women(patient_list).map { |woman| woman['person_id'].to_i }
|
41
|
+
return { FP: pregnant, FBf: [] } if (patient_list - pregnant).blank?
|
42
|
+
|
43
|
+
feeding = breast_feeding(patient_list - pregnant).map { |woman| woman['person_id'].to_i }
|
44
|
+
|
45
|
+
{
|
46
|
+
FP: pregnant,
|
47
|
+
FBf: feeding
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
# rubocop:disable Metrics/AbcSize
|
52
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
53
|
+
# rubocop:disable Metrics/MethodLength
|
54
|
+
def process_due_people
|
55
|
+
@clients = []
|
56
|
+
start = Time.now
|
57
|
+
results = clients_on_art
|
58
|
+
# get all clients that are females from results
|
59
|
+
@maternal_status = vl_maternal_status(results.map do |patient|
|
60
|
+
patient['patient_id'] if patient['gender'] == 'F'
|
61
|
+
end.compact)
|
62
|
+
if @type.blank? || @type == 'poc'
|
63
|
+
Parallel.each(results, in_threads: 20) do |patient|
|
64
|
+
process_client_eligibility(patient)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
results.each { |patient| process_client_eligibility(patient) } if @type == 'emastercard'
|
68
|
+
end_time = Time.now
|
69
|
+
Rails.logger.info "Time taken to process #{results.length} clients: #{end_time - start} seconds.
|
70
|
+
These are the clients returned: #{@clients.length}"
|
71
|
+
@clients
|
72
|
+
end
|
73
|
+
# rubocop:enable Metrics/AbcSize
|
74
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
75
|
+
# rubocop:enable Metrics/MethodLength
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
# rubocop:disable Metrics/AbcSize
|
80
|
+
# rubocop:disable Metrics/MethodLength
|
81
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
82
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
83
|
+
# rubocop:disable Layout/LineLength
|
84
|
+
def process_client_eligibility(patient)
|
85
|
+
result = extra_information(patient['patient_id'])
|
86
|
+
patient['defaulter_date'] = result['defaulter_date']
|
87
|
+
patient['current_regimen'] = result['current_regimen']
|
88
|
+
patient['art_start_date'] = result['art_start_date']
|
89
|
+
patient['maternal_status'] =
|
90
|
+
if @maternal_status[:FP].include?(patient['patient_id'])
|
91
|
+
'FP'
|
92
|
+
else
|
93
|
+
(@maternal_status[:FBf].include?(patient['patient_id']) ? 'FBf' : nil)
|
94
|
+
end
|
95
|
+
return if !patient['defaulter_date'].blank? && (patient['defaulter_date'].to_date < end_date - 12.months)
|
96
|
+
return if result['art_start_date'].blank?
|
97
|
+
return if result['art_start_date'].to_date > end_date - 6.months
|
98
|
+
return if remove_adverse_outcome_patient?(patient)
|
99
|
+
|
100
|
+
@clients << patient
|
101
|
+
end
|
102
|
+
|
103
|
+
def remove_adverse_outcome_patient?(patient)
|
104
|
+
return false unless adverse_outcomes.include?(patient['state'].to_i)
|
105
|
+
|
106
|
+
last_date = patient['vl_order_date'] || patient['art_start_date']
|
107
|
+
if patient['vl_order_date'].present? && last_date.to_date >= start_date && last_date.to_date <= end_date
|
108
|
+
return false
|
109
|
+
end
|
110
|
+
|
111
|
+
length = 12
|
112
|
+
length = 6 if patient['maternal_status'] == 'FP'
|
113
|
+
length = 6 if patient['maternal_status'] == 'FBf'
|
114
|
+
length = 6 if patient['current_regimen'].to_s.match(/P/i)
|
115
|
+
|
116
|
+
if patient['vl_order_date'] && patient['vl_order_date'].to_date >= end_date - 12.months && patient['vl_order_date'].to_date <= end_date
|
117
|
+
return false
|
118
|
+
end
|
119
|
+
return false if last_date.to_date + length.months < patient['outcome_date'].to_date
|
120
|
+
|
121
|
+
true
|
122
|
+
end
|
123
|
+
|
124
|
+
def pregnant_women(patient_list)
|
125
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
126
|
+
SELECT o.person_id, o.value_coded
|
127
|
+
FROM obs o
|
128
|
+
INNER JOIN encounter e ON e.encounter_id = o.encounter_id AND e.voided = 0 AND e.encounter_type IN (#{encounter_types.to_sql})
|
129
|
+
INNER JOIN person p ON o.person_id = e.patient_id AND LEFT(p.gender, 1) = 'F'
|
130
|
+
#{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
131
|
+
INNER JOIN (
|
132
|
+
SELECT person_id, MAX(obs_datetime) AS obs_datetime
|
133
|
+
FROM obs
|
134
|
+
INNER JOIN encounter ON encounter.encounter_id = obs.encounter_id AND encounter.encounter_type IN (#{encounter_types.to_sql}) AND encounter.voided = 0
|
135
|
+
WHERE obs.concept_id IN (#{pregnant_concepts.to_sql})
|
136
|
+
AND obs.obs_datetime BETWEEN DATE(#{ActiveRecord::Base.connection.quote(start_date)}) AND DATE(#{ActiveRecord::Base.connection.quote(end_date)}) + INTERVAL 1 DAY
|
137
|
+
AND obs.voided = 0
|
138
|
+
#{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
139
|
+
GROUP BY person_id
|
140
|
+
) AS max_obs ON max_obs.person_id = o.person_id AND max_obs.obs_datetime = o.obs_datetime
|
141
|
+
WHERE o.concept_id IN (#{pregnant_concepts.to_sql})
|
142
|
+
AND o.voided = 0
|
143
|
+
AND o.value_coded IN (#{yes_concepts.join(',')})
|
144
|
+
AND o.person_id IN (#{patient_list.join(',')})
|
145
|
+
#{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
146
|
+
GROUP BY o.person_id
|
147
|
+
SQL
|
148
|
+
end
|
149
|
+
|
150
|
+
def breast_feeding(patient_list)
|
151
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
152
|
+
SELECT o.person_id, o.value_coded
|
153
|
+
FROM obs o
|
154
|
+
INNER JOIN encounter e ON e.encounter_id = o.encounter_id AND e.voided = 0 AND e.encounter_type IN (#{encounter_types.to_sql})
|
155
|
+
INNER JOIN person p ON o.person_id = e.patient_id AND LEFT(p.gender, 1) = 'F'
|
156
|
+
#{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
157
|
+
INNER JOIN (
|
158
|
+
SELECT person_id, MAX(obs_datetime) AS obs_datetime
|
159
|
+
FROM obs
|
160
|
+
INNER JOIN encounter ON encounter.encounter_id = obs.encounter_id AND encounter.encounter_type IN (#{encounter_types.to_sql}) AND encounter.voided = 0
|
161
|
+
WHERE obs.concept_id IN (#{breast_feeding_concepts.to_sql})
|
162
|
+
AND obs.obs_datetime BETWEEN DATE(#{ActiveRecord::Base.connection.quote(start_date)}) AND DATE(#{ActiveRecord::Base.connection.quote(end_date)}) + INTERVAL 1 DAY
|
163
|
+
AND obs.voided = 0
|
164
|
+
#{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
165
|
+
GROUP BY person_id
|
166
|
+
) AS max_obs ON max_obs.person_id = o.person_id AND max_obs.obs_datetime = o.obs_datetime
|
167
|
+
WHERE o.concept_id IN (#{breast_feeding_concepts.to_sql})
|
168
|
+
AND o.voided = 0
|
169
|
+
AND o.value_coded IN (#{yes_concepts.join(',')})
|
170
|
+
AND o.person_id IN (#{patient_list.join(',')})
|
171
|
+
#{site_manager(operator: 'AND', column: 'o.site_id', location: @location)}
|
172
|
+
GROUP BY o.person_id
|
173
|
+
SQL
|
174
|
+
end
|
175
|
+
|
176
|
+
def build_report(report)
|
177
|
+
refresh_outcomes_table
|
178
|
+
load_tx_curr_into_report(report, create_patients_alive_and_on_art_query)
|
179
|
+
clients = process_due_people
|
180
|
+
clients.each do |patient|
|
181
|
+
report[patient['age_group']][patient['gender'].to_sym][:due_for_vl] << patient['patient_id']
|
182
|
+
end
|
183
|
+
load_patient_tests_into_report(report, clients.map { |patient| patient['patient_id'] })
|
184
|
+
end
|
185
|
+
|
186
|
+
def refresh_outcomes_table
|
187
|
+
MalawiHivProgramReports::Moh::CohortBuilder.new(outcomes_definition: 'pepfar', location: @location)
|
188
|
+
.init_temporary_tables(@start_date, @end_date, nil)
|
189
|
+
end
|
190
|
+
|
191
|
+
def create_patients_alive_and_on_art_query
|
192
|
+
ActiveRecord::Base.connection.select_all(
|
193
|
+
<<~SQL
|
194
|
+
SELECT tpo.patient_id, LEFT(tesd.gender, 1) AS gender, disaggregated_age_group(tesd.birthdate, DATE('#{end_date.to_date}')) age_group
|
195
|
+
FROM temp_patient_outcomes tpo
|
196
|
+
INNER JOIN temp_earliest_start_date tesd ON tesd.patient_id = tpo.patient_id
|
197
|
+
WHERE tpo.cum_outcome = 'On antiretrovirals'
|
198
|
+
SQL
|
199
|
+
)
|
200
|
+
end
|
201
|
+
|
202
|
+
def load_tx_curr_into_report(report, patients)
|
203
|
+
report.each do |age_group, _data|
|
204
|
+
%i[M F].each do |gender|
|
205
|
+
report[age_group][gender][:tx_curr] ||= []
|
206
|
+
report[age_group][gender][:tx_curr] = populate_tx_curr(patients, age_group, gender) || []
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def populate_tx_curr(patients, age_group, gender)
|
212
|
+
patients.select do |patient|
|
213
|
+
(patient['age_group'] == age_group && patient['gender'].to_sym == gender) && patient['patient_id']
|
214
|
+
end&.map {|a| a['patient_id']}
|
215
|
+
end
|
216
|
+
|
217
|
+
# rubocop:disable Metrics/AbcSize
|
218
|
+
# rubocop:disable Metrics/MethodLength
|
219
|
+
def load_patient_tests_into_report(report, clients)
|
220
|
+
find_patients_with_viral_load(clients).each do |patient|
|
221
|
+
age_group = patient['age_group']
|
222
|
+
gender = patient['gender'].to_sym
|
223
|
+
reason_for_test = (patient['reason_for_test'] || 'Routine').match?(/Routine/i) ? :routine : :targeted
|
224
|
+
|
225
|
+
report[age_group][gender][:drawn][reason_for_test] << patient['patient_id']
|
226
|
+
next unless patient['result_value']
|
227
|
+
|
228
|
+
if patient['result_value'].casecmp?('LDL')
|
229
|
+
report[age_group][gender][:low_vl][reason_for_test] << patient['patient_id']
|
230
|
+
elsif patient['result_value'].to_i < 1000
|
231
|
+
report[age_group][gender][:low_vl][reason_for_test] << patient['patient_id']
|
232
|
+
else
|
233
|
+
report[age_group][gender][:high_vl][reason_for_test] << patient['patient_id']
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
# rubocop:enable Metrics/AbcSize
|
238
|
+
# rubocop:enable Metrics/MethodLength
|
239
|
+
|
240
|
+
## This method prepares the response structure for the report
|
241
|
+
def init_report
|
242
|
+
pepfar_age_groups.each_with_object({}) do |age_group, report|
|
243
|
+
report[age_group] = %i[F M].each_with_object({}) do |gender, hash|
|
244
|
+
hash[gender] = {
|
245
|
+
due_for_vl: [],
|
246
|
+
drawn: { routine: [], targeted: [] },
|
247
|
+
high_vl: { routine: [], targeted: [] },
|
248
|
+
low_vl: { routine: [], targeted: [] }
|
249
|
+
}
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def due_for_viral_load
|
255
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
256
|
+
(#{find_patients_with_overdue_viral_load}) UNION (#{find_patients_due_for_initial_viral_load})
|
257
|
+
SQL
|
258
|
+
end
|
259
|
+
|
260
|
+
def adverse_outcomes
|
261
|
+
@adverse_outcomes ||= ActiveRecord::Base.connection.select_all(
|
262
|
+
<<~SQL
|
263
|
+
SELECT pws.program_workflow_state_id state
|
264
|
+
FROM program_workflow pw
|
265
|
+
INNER JOIN concept_name pcn ON pcn.concept_id = pw.concept_id AND pcn.concept_name_type = 'FULLY_SPECIFIED' AND pcn.voided = 0
|
266
|
+
INNER JOIN program_workflow_state pws ON pws.program_workflow_id = pw.program_workflow_id AND pws.retired = 0
|
267
|
+
INNER JOIN concept_name cn ON cn.concept_id = pws.concept_id AND cn.concept_name_type = 'FULLY_SPECIFIED' AND cn.voided = 0
|
268
|
+
WHERE pw.program_id = 1 AND pw.retired = 0 AND pws.terminal = 1
|
269
|
+
SQL
|
270
|
+
).map { |state| state['state'] }
|
271
|
+
end
|
272
|
+
|
273
|
+
def clients_on_art
|
274
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
275
|
+
SELECT
|
276
|
+
ab.patient_id,
|
277
|
+
disaggregated_age_group(p.birthdate, DATE(#{ActiveRecord::Base.connection.quote(end_date)})) AS age_group,
|
278
|
+
p.birthdate,
|
279
|
+
p.gender,
|
280
|
+
pid.identifier AS arv_number,
|
281
|
+
current_state.state,
|
282
|
+
current_state.start_date outcome_date,
|
283
|
+
current_order.start_date vl_order_date
|
284
|
+
FROM orders ab
|
285
|
+
INNER JOIN person p ON p.person_id = ab.patient_id AND p.voided = 0
|
286
|
+
#{site_manager(operator: 'AND', column: 'p.site_id', location: @location)}
|
287
|
+
INNER JOIN drug_order dor ON dor.order_id = ab.order_id AND dor.quantity > 0
|
288
|
+
INNER JOIN arv_drug ad ON dor.drug_inventory_id = ad.drug_id
|
289
|
+
INNER JOIN patient_program pp ON pp.patient_id = ab.patient_id AND pp.voided = 0 AND pp.program_id = 1
|
290
|
+
INNER JOIN (
|
291
|
+
SELECT a.patient_program_id, a.state, a.start_date, a.end_date
|
292
|
+
FROM patient_state a
|
293
|
+
LEFT OUTER JOIN patient_state b ON a.patient_program_id = b.patient_program_id
|
294
|
+
AND a.start_date < b.start_date
|
295
|
+
AND b.voided = 0
|
296
|
+
WHERE b.patient_program_id IS NULL AND a.end_date IS NULL AND a.voided = 0
|
297
|
+
) current_state ON current_state.patient_program_id = pp.patient_program_id
|
298
|
+
LEFT OUTER JOIN orders b ON ab.patient_id = b.patient_id
|
299
|
+
AND ab.order_id = b.order_id
|
300
|
+
AND ab.auto_expire_date < b.auto_expire_date
|
301
|
+
AND b.voided = 0 AND b.order_type_id = 1
|
302
|
+
#{site_manager(operator: 'AND', column: 'b.site_id', location: @location)}
|
303
|
+
LEFT JOIN (#{current_occupation_query}) a ON a.person_id = ab.patient_id
|
304
|
+
#{site_manager(operator: 'AND', column: 'a.site_id', location: @location)}
|
305
|
+
LEFT JOIN patient_identifier pid ON pid.patient_id = pp.patient_id AND pid.identifier_type IN (#{pepfar_patient_identifier_type.to_sql}) AND pid.voided = 0
|
306
|
+
#{site_manager(operator: 'AND', column: 'pid.site_id', location: @location)}
|
307
|
+
LEFT JOIN (
|
308
|
+
SELECT ab.patient_id, MAX(ab.start_date) start_date
|
309
|
+
FROM orders ab
|
310
|
+
INNER JOIN concept_name
|
311
|
+
ON concept_name.concept_id = ab.concept_id
|
312
|
+
AND concept_name.name IN ('Blood', 'DBS (Free drop to DBS card)', 'DBS (Using capillary tube)', '50:50 Normal Plasma')
|
313
|
+
AND concept_name.voided = 0
|
314
|
+
#{site_manager(operator: 'AND', column: 'ab.site_id', location: @location)}
|
315
|
+
LEFT OUTER JOIN orders b ON ab.patient_id = b.patient_id
|
316
|
+
AND ab.order_id = b.order_id
|
317
|
+
AND ab.start_date < b.start_date
|
318
|
+
AND b.voided = 0
|
319
|
+
#{site_manager(operator: 'AND', column: 'b.site_id', location: @location)}
|
320
|
+
WHERE b.patient_id IS NULL AND ab.voided = 0 AND ab.order_type_id = 4 AND ab.start_date < DATE(#{ActiveRecord::Base.connection.quote(end_date)}) + INTERVAL 1 DAY
|
321
|
+
GROUP BY ab.patient_id
|
322
|
+
) current_order ON current_order.patient_id = ab.patient_id
|
323
|
+
WHERE b.patient_id IS NULL
|
324
|
+
AND ab.voided = 0 #{%w[Military Civilian].include?(@occupation) ? 'AND' : ''} #{occupation_filter(occupation: @occupation, field_name: 'value', table_name: 'a', include_clause: false)}
|
325
|
+
AND ab.start_date < DATE(#{ActiveRecord::Base.connection.quote(end_date)}) + INTERVAL 1 DAY
|
326
|
+
AND p.person_id NOT IN (#{drug_refills_and_external_consultation_list})
|
327
|
+
#{site_manager(operator: 'AND', column: 'ab.site_id', location: @location)}
|
328
|
+
AND ((current_state.state IN (#{adverse_outcomes.join(',')}) AND current_state.start_date >= (DATE(#{ActiveRecord::Base.connection.quote(end_date)}) - INTERVAL 12 MONTH)) OR current_state.state IN (7, 1, 87, 120, 136))
|
329
|
+
GROUP BY ab.patient_id;
|
330
|
+
SQL
|
331
|
+
end
|
332
|
+
|
333
|
+
def extra_information(patient_id)
|
334
|
+
ActiveRecord::Base.connection.select_one <<~SQL
|
335
|
+
SELECT #{function_manager(function: 'patient_current_regimen', location: @location, args: "#{patient_id}, DATE(#{ActiveRecord::Base.connection.quote(end_date)}), #{@location}")} AS current_regimen,
|
336
|
+
#{function_manager(function: 'date_antiretrovirals_started', location: @location, args: "#{patient_id}, DATE(#{ActiveRecord::Base.connection.quote(end_date)}), #{@location}")} AS art_start_date,
|
337
|
+
#{function_manager(function: 'current_pepfar_defaulter_date', location: @location, args: "#{patient_id}, DATE(#{ActiveRecord::Base.connection.quote(end_date)}), #{@location}")} AS defaulter_date
|
338
|
+
SQL
|
339
|
+
end
|
340
|
+
|
341
|
+
##
|
342
|
+
# Find all patients that are on treatment with at least one VL before end of reporting period.
|
343
|
+
def find_patients_with_viral_load(clients)
|
344
|
+
ActiveRecord::Base.connection.select_all <<~SQL
|
345
|
+
SELECT orders.patient_id,
|
346
|
+
disaggregated_age_group(patient.birthdate,
|
347
|
+
DATE(#{ActiveRecord::Base.connection.quote(end_date)})) AS age_group,
|
348
|
+
patient.birthdate,
|
349
|
+
patient.gender,
|
350
|
+
patient_identifier.identifier AS arv_number,
|
351
|
+
orders.start_date AS order_date,
|
352
|
+
COALESCE(orders.discontinued_date, orders.start_date) AS sample_draw_date,
|
353
|
+
COALESCE(reason_for_test_value.name, reason_for_test.value_text) AS reason_for_test,
|
354
|
+
result.value_modifier AS result_modifier,
|
355
|
+
COALESCE(result.value_numeric, result.value_text) AS result_value
|
356
|
+
FROM orders
|
357
|
+
INNER JOIN person patient ON patient.person_id = orders.patient_id AND patient.voided = 0
|
358
|
+
#{site_manager(operator: 'AND', column: 'patient.site_id', location: @location)}
|
359
|
+
INNER JOIN order_type
|
360
|
+
ON order_type.order_type_id = orders.order_type_id
|
361
|
+
AND order_type.name = 'Lab'
|
362
|
+
AND order_type.retired = 0
|
363
|
+
INNER JOIN concept_name
|
364
|
+
ON concept_name.concept_id = orders.concept_id
|
365
|
+
AND concept_name.name IN ('Blood', 'DBS (Free drop to DBS card)', 'DBS (Using capillary tube)', 'Plasma')
|
366
|
+
AND concept_name.voided = 0
|
367
|
+
LEFT JOIN obs AS reason_for_test
|
368
|
+
ON reason_for_test.order_id = orders.order_id
|
369
|
+
AND reason_for_test.concept_id IN (SELECT concept_id FROM concept_name WHERE name LIKE 'Reason for test' AND voided = 0)
|
370
|
+
AND reason_for_test.voided = 0
|
371
|
+
LEFT JOIN concept_name AS reason_for_test_value
|
372
|
+
ON reason_for_test_value.concept_id = reason_for_test.value_coded
|
373
|
+
AND reason_for_test_value.voided = 0
|
374
|
+
LEFT JOIN obs AS result
|
375
|
+
ON result.order_id = orders.order_id
|
376
|
+
AND result.concept_id IN (SELECT concept_id FROM concept_name WHERE name LIKE 'HIV Viral load' AND voided = 0)
|
377
|
+
AND result.voided = 0
|
378
|
+
AND (result.value_text IS NOT NULL OR result.value_numeric IS NOT NULL)
|
379
|
+
INNER JOIN (
|
380
|
+
/* Get the latest order dates for each patient */
|
381
|
+
SELECT orders.patient_id, MAX(orders.start_date) AS start_date
|
382
|
+
FROM orders
|
383
|
+
INNER JOIN order_type
|
384
|
+
ON order_type.order_type_id = orders.order_type_id
|
385
|
+
AND order_type.name = 'Lab'
|
386
|
+
AND order_type.retired = 0
|
387
|
+
INNER JOIN concept_name
|
388
|
+
ON concept_name.concept_id = orders.concept_id
|
389
|
+
AND concept_name.name IN ('Blood', 'DBS (Free drop to DBS card)', 'DBS (Using capillary tube)', 'Plasma')
|
390
|
+
AND concept_name.voided = 0
|
391
|
+
WHERE orders.start_date < DATE(#{ActiveRecord::Base.connection.quote(end_date)}) + INTERVAL 1 DAY
|
392
|
+
#{site_manager(operator: 'AND', column: 'orders.site_id', location: @location)}
|
393
|
+
AND orders.start_date >= DATE(#{ActiveRecord::Base.connection.quote(start_date)}) - INTERVAL 12 MONTH
|
394
|
+
AND orders.voided = 0
|
395
|
+
GROUP BY orders.patient_id
|
396
|
+
) AS latest_patient_order_date
|
397
|
+
ON latest_patient_order_date.patient_id = orders.patient_id
|
398
|
+
AND latest_patient_order_date.start_date = orders.start_date
|
399
|
+
LEFT JOIN patient_identifier
|
400
|
+
ON patient_identifier.patient_id = orders.patient_id
|
401
|
+
AND patient_identifier.identifier_type IN (#{pepfar_patient_identifier_type.to_sql})
|
402
|
+
AND patient_identifier.voided = 0
|
403
|
+
WHERE orders.start_date < DATE(#{ActiveRecord::Base.connection.quote(end_date)}) + INTERVAL 1 DAY
|
404
|
+
AND orders.start_date >= DATE(#{ActiveRecord::Base.connection.quote(start_date)}) - INTERVAL 12 MONTH
|
405
|
+
AND orders.voided = 0
|
406
|
+
AND orders.patient_id IN (#{clients.push(0).join(',')})
|
407
|
+
GROUP BY orders.patient_id
|
408
|
+
SQL
|
409
|
+
end
|
410
|
+
|
411
|
+
def yes_concepts
|
412
|
+
@yes_concepts ||= ::ConceptName.where(name: 'Yes').select(:concept_id).map do |record|
|
413
|
+
record['concept_id'].to_i
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
def pregnant_concepts
|
418
|
+
@pregnant_concepts ||= ::ConceptName.where(name: ['Is patient pregnant?', 'patient pregnant'])
|
419
|
+
.select(:concept_id)
|
420
|
+
end
|
421
|
+
|
422
|
+
def breast_feeding_concepts
|
423
|
+
@breast_feeding_concepts ||= ::ConceptName.where(name: ['Breast feeding?', 'Breast feeding', 'Breastfeeding'])
|
424
|
+
.select(:concept_id)
|
425
|
+
end
|
426
|
+
|
427
|
+
def encounter_types
|
428
|
+
@encounter_types ||= ::EncounterType.where(name: ['HIV CLINIC CONSULTATION', 'HIV STAGING'])
|
429
|
+
.select(:encounter_type_id)
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MalawiHivProgramReports
|
4
|
+
module ReportMap
|
5
|
+
REPORTS = {
|
6
|
+
'ARCHIVING_CANDIDATES' => MalawiHivProgramReports::ArchivingCandidates,
|
7
|
+
'APPOINTMENTS' => MalawiHivProgramReports::Clinic::AppointmentsReport,
|
8
|
+
'ARV_REFILL_PERIODS' => MalawiHivProgramReports::ArvRefillPeriods,
|
9
|
+
'COHORT' => MalawiHivProgramReports::Moh::Cohort,
|
10
|
+
'COHORT_DISAGGREGATED' => MalawiHivProgramReports::Moh::CohortDisaggregated,
|
11
|
+
'COHORT_DISAGGREGATED_ADDITIONS' => MalawiHivProgramReports::Moh::CohortDisaggregatedAdditions,
|
12
|
+
'COHORT_SURVIVAL_ANALYSIS' => MalawiHivProgramReports::Moh::CohortSurvivalAnalysis,
|
13
|
+
'DRUG_DISPENSATIONS' => MalawiHivProgramReports::Clinic::DrugDispensations,
|
14
|
+
'HIGH_VL_PATIENTS' => MalawiHivProgramReports::Clinic::ViralLoadResults,
|
15
|
+
'IPT' => MalawiHivProgramReports::Clinic::IptReport,
|
16
|
+
'PATIENTS_WITH_OUTDATED_DEMOGRAPHICS' => MalawiHivProgramReports::Clinic::PatientsWithOutdatedDemographics,
|
17
|
+
'PATIENTS_ON_DTG' => MalawiHivProgramReports::Clinic::PatientsOnDtg,
|
18
|
+
'PREGNANT_PATIENTS' => MalawiHivProgramReports::Clinic::PregnantPatients,
|
19
|
+
'REGIMENS_AND_FORMULATIONS' => MalawiHivProgramReports::Clinic::RegimensAndFormulations,
|
20
|
+
'REGIMENS_BY_WEIGHT_AND_GENDER' => MalawiHivProgramReports::Clinic::RegimensByWeightAndGender,
|
21
|
+
'REGIMEN_SWITCH' => MalawiHivProgramReports::Clinic::RegimenSwitch,
|
22
|
+
'REGIMEN_REPORT' => MalawiHivProgramReports::Clinic::RegimenDispensationData,
|
23
|
+
'PEPFAR_REGIMEN_SWITCH' => MalawiHivProgramReports::Pepfar::RegimenSwitch,
|
24
|
+
'RETENTION' => MalawiHivProgramReports::Clinic::Retention,
|
25
|
+
'LIMS_ELECTRONIC_RESULTS' => MalawiHivProgramReports::Clinic::LimsResults,
|
26
|
+
'TPT_OUTCOME' => MalawiHivProgramReports::Clinic::TptOutcome,
|
27
|
+
'CLINIC_TX_RTT' => MalawiHivProgramReports::Clinic::TxRtt,
|
28
|
+
'TB_PREV2' => MalawiHivProgramReports::Pepfar::TbPrev3,
|
29
|
+
'TPT_NEWLY_INITIATED' => MalawiHivProgramReports::Moh::TptNewlyInitiated,
|
30
|
+
'TX_CURR' => MalawiHivProgramReports::Clinic::PatientsAliveAndOnTreatment,
|
31
|
+
'TX_ML' => MalawiHivProgramReports::Pepfar::TxMl,
|
32
|
+
'TX_RTT' => MalawiHivProgramReports::Pepfar::TxRtt,
|
33
|
+
'IPT_COVERAGE' => MalawiHivProgramReports::Clinic::IptCoverage,
|
34
|
+
'VISITS' => MalawiHivProgramReports::Clinic::VisitsReport,
|
35
|
+
'VL_DUE' => MalawiHivProgramReports::Clinic::PatientsDueForViralLoad,
|
36
|
+
'DEFAULTER_LIST' => MalawiHivProgramReports::Pepfar::DefaulterList,
|
37
|
+
'VL_DISAGGREGATED' => MalawiHivProgramReports::Clinic::ViralLoadDisaggregated,
|
38
|
+
'TB_PREV' => MalawiHivProgramReports::Pepfar::TbPrev,
|
39
|
+
'OUTCOME_LIST' => MalawiHivProgramReports::Clinic::OutcomeList,
|
40
|
+
'VIRAL_LOAD' => MalawiHivProgramReports::Clinic::ViralLoad,
|
41
|
+
'VIRAL_LOAD_COVERAGE' => MalawiHivProgramReports::Pepfar::ViralLoadCoverage2,
|
42
|
+
'VL_MATERNAL_STATUS' => MalawiHivProgramReports::Pepfar::MaternalStatus,
|
43
|
+
'EXTERNAL_CONSULTATION_CLIENTS' => MalawiHivProgramReports::Clinic::ExternalConsultationClients,
|
44
|
+
'SC_ARVDISP' => MalawiHivProgramReports::Pepfar::ScArvdisp,
|
45
|
+
'SC_CURR' => MalawiHivProgramReports::Pepfar::ScCurr,
|
46
|
+
'PATIENT_ART_VL_DATES' => MalawiHivProgramReports::Pepfar::PatientStartVl,
|
47
|
+
'MOH_TPT' => MalawiHivProgramReports::Moh::MohTpt,
|
48
|
+
'TX_TB' => MalawiHivProgramReports::Pepfar::TxTb,
|
49
|
+
'VL_COLLECTION' => MalawiHivProgramReports::Clinic::VlCollection,
|
50
|
+
'DISCREPANCY_REPORT' => MalawiHivProgramReports::Clinic::DiscrepancyReport,
|
51
|
+
'STOCK_CARD' => MalawiHivProgramReports::Clinic::StockCardReport,
|
52
|
+
'HYPERTENSION_REPORT' => MalawiHivProgramReports::Clinic::HypertensionReport,
|
53
|
+
'TX_NEW' => MalawiHivProgramReports::Pepfar::TxNew
|
54
|
+
}.freeze
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# About UTILS
|
2
|
+
This folder contains all the utility functions that are used in the project. The functions are divided into different files based on their functionality. The files are as follows:
|
3
|
+
|
4
|
+
- [Common SQL Queries Utils](docs/common_sql_query_utils.md) - This file contains all the common SQL queries that are used in the project.
|
5
|
+
- [Concurrency Utils]() - This file contains all the functions that are used to handle concurrency in the project.
|
6
|
+
- [Model Utils]() - This file contains all the functions that are used to handle the models in the project.
|
7
|
+
- [Parameter Utils]() - This file contains all the functions that are used to handle the parameters in the project.
|
8
|
+
- [Time Utils]() - This file contains all the functions that are used to handle the time in the project.
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This is a module that can be included in any class that needs to use the methods defined here.
|
4
|
+
module MalawiHivProgramReports
|
5
|
+
module Utils
|
6
|
+
module CommonSqlQueryUtils
|
7
|
+
def process_occupation(start_date:, end_date:, occupation:, location:, definition: 'moh')
|
8
|
+
return if occupation.blank?
|
9
|
+
|
10
|
+
MalawiHivProgramReports::Moh::CohortBuilder.new(outcomes_definition: definition, location:).init_temporary_tables(start_date, end_date,
|
11
|
+
occupation)
|
12
|
+
end
|
13
|
+
|
14
|
+
def occupation_filter(occupation:, field_name:, table_name: '', include_clause: true)
|
15
|
+
clause = 'WHERE' if include_clause
|
16
|
+
table_name = "#{table_name}." unless table_name.blank?
|
17
|
+
return '' if occupation.blank?
|
18
|
+
return '' if occupation == 'All'
|
19
|
+
if occupation == 'Military'
|
20
|
+
return "#{clause} #{table_name}#{field_name} IN ('#{occupation}', 'MDF Reserve', 'MDF Retired', 'Soldier', 'Soldier/Police')"
|
21
|
+
end
|
22
|
+
return unless occupation == 'Civilian'
|
23
|
+
|
24
|
+
"#{clause} #{table_name}#{field_name} NOT IN ('Military', 'MDF Reserve', 'MDF Retired', 'Soldier', 'Soldier/Police')"
|
25
|
+
end
|
26
|
+
|
27
|
+
def external_client_query(end_date:)
|
28
|
+
end_date = ActiveRecord::Base.connection.quote(end_date)
|
29
|
+
<<~SQL
|
30
|
+
SELECT obs.person_id FROM obs,
|
31
|
+
(SELECT person_id, Max(obs_datetime) AS obs_datetime, concept_id FROM obs
|
32
|
+
WHERE concept_id IN (SELECT concept_id FROM concept_name WHERE name = 'Type of patient' AND voided = 0)
|
33
|
+
AND DATE(obs_datetime) <= #{end_date}
|
34
|
+
AND voided = 0
|
35
|
+
GROUP BY person_id,concept_id) latest_record
|
36
|
+
WHERE obs.person_id = latest_record.person_id
|
37
|
+
AND obs.concept_id = latest_record.concept_id
|
38
|
+
AND obs.obs_datetime = latest_record.obs_datetime
|
39
|
+
AND obs.value_coded IN (SELECT concept_id FROM concept_name WHERE name = 'Drug refill' || name = 'External consultation')
|
40
|
+
AND obs.voided = 0
|
41
|
+
#{site_manager(operator: 'AND', column: 'obs.site_id', location: @location)}
|
42
|
+
SQL
|
43
|
+
end
|
44
|
+
|
45
|
+
def current_occupation_query
|
46
|
+
ActiveRecord::Base.connection.adapter_name.downcase
|
47
|
+
<<~SQL
|
48
|
+
SELECT a.person_id, a.value, a.site_id
|
49
|
+
FROM person_attribute a
|
50
|
+
LEFT OUTER JOIN person_attribute b
|
51
|
+
ON a.person_attribute_id = b.person_attribute_id #{site_manager(operator: 'AND', column: 'b.site_id', location: @location)}
|
52
|
+
AND a.date_created < b.date_created
|
53
|
+
AND b.voided = 0 #{site_manager(operator: 'AND', column: 'a.site_id', location: @location)}
|
54
|
+
WHERE b.person_attribute_id IS NULL AND a.person_attribute_type_id = 13 AND a.voided = 0
|
55
|
+
#{site_manager(operator: 'AND', column: 'a.site_id', location: @location)}
|
56
|
+
SQL
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MalawiHivProgramReports
|
4
|
+
module Utils
|
5
|
+
module ConcurrencyUtils
|
6
|
+
LOCK_FILES_DIR_PATH = Rails.root.join('tmp', 'locks')
|
7
|
+
|
8
|
+
##
|
9
|
+
# Acquire a lock and run the given block of code.
|
10
|
+
#
|
11
|
+
# The locking mechanism uses files to allow for blocking across processes.
|
12
|
+
# This is useful for example in situations where you only want to run one
|
13
|
+
# instance of a report.
|
14
|
+
#
|
15
|
+
# Parameters:
|
16
|
+
# lock_file_path: A relative path to the lock file (allows for namespacing your locks, eg art_service/regimens.lock)
|
17
|
+
# blocking: If lock can not be acquired wait else throw an error (defaults to true)
|
18
|
+
#
|
19
|
+
# Raises:
|
20
|
+
# FailedToAcquireLock: When lock couldn't be acquired and blocking is set to false
|
21
|
+
#
|
22
|
+
# Usage:
|
23
|
+
# class Someclass
|
24
|
+
# include ModelUtils
|
25
|
+
#
|
26
|
+
# def do_something
|
27
|
+
# with_lock('mylockfile.lock') do
|
28
|
+
# # Run some task requiring exclusive access to some resource
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# SomeClass.new.do_something
|
34
|
+
def with_lock(lock_file_path, blocking: true)
|
35
|
+
path = LOCK_FILES_DIR_PATH.join(lock_file_path)
|
36
|
+
|
37
|
+
unless Dir.exist?(path.dirname)
|
38
|
+
Rails.logger.debug("Creating lock file directory: #{path.dirname}")
|
39
|
+
::FileUtils.mkdir_p(path.dirname)
|
40
|
+
end
|
41
|
+
|
42
|
+
File.open(path, 'w') do |lock_file|
|
43
|
+
Rails.logger.debug("Attempting to acquire lock: #{lock_file_path}")
|
44
|
+
locked = lock_file.flock(blocking ? File::LOCK_EX : File::LOCK_NB | File::LOCK_EX)
|
45
|
+
raise ::FailedToAcquireLock, "Lock #{lock_file_path} is locked by another process" if !locked && !blocking
|
46
|
+
|
47
|
+
lock_file.write("Locked by process ##{Process.pid} at #{Time.now}")
|
48
|
+
yield
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Code Documentation
|
2
|
+
## Summary
|
3
|
+
The documentation is for a module called ```CommonSqlQueryUtils``` that provides two methods: ```occupation_filter``` and ```external_client_query```. The ```occupation_filter``` method takes in parameters such as occupation, field_name, table_name, and include_clause, and returns a SQL clause based on the occupation value. The ```external_client_query``` method takes in the end_date parameter and returns a SQL query that retrieves person_ids from the ```obs``` table based on certain conditions.
|
4
|
+
|
5
|
+
Example Usage
|
6
|
+
### Example usage of the ```occupation_filter``` method
|
7
|
+
```ruby
|
8
|
+
include CommonSqlQueryUtils
|
9
|
+
|
10
|
+
|
11
|
+
occupation_filter(occupation: 'Military', field_name: 'occupation', table_name: 'users', include_clause: true)
|
12
|
+
```
|
13
|
+
#### Output: "WHERE users.occupation = 'Military'"
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
occupation_filter(occupation: 'Civilian', field_name: 'occupation', table_name: 'users', include_clause: false)
|
17
|
+
```
|
18
|
+
#### Output: "users.occupation != 'Military'"
|
19
|
+
|
20
|
+
### Example usage of the ```external_client_query``` method
|
21
|
+
```ruby
|
22
|
+
include CommonSqlQueryUtils
|
23
|
+
|
24
|
+
external_client_query(end_date: '2021-01-01')
|
25
|
+
```
|
26
|
+
#### Output: SQL query string
|
27
|
+
|
28
|
+
## Code Analysis
|
29
|
+
### Inputs
|
30
|
+
- ```occupation``` (string): The occupation value to filter on.
|
31
|
+
- ```field_name``` (string): The name of the field to filter on.
|
32
|
+
- ```table_name``` (string): The name of the table to include in the SQL clause.
|
33
|
+
- ```include_clause``` (boolean): Whether to include the ```WHERE``` clause in the SQL statement.
|
34
|
+
- ```end_date``` (string): The end date to use in the SQL query.
|
35
|
+
|
36
|
+
### Flow
|
37
|
+
The ```occupation_filter``` method checks if the ```include_clause``` parameter is true and sets the ```clause``` variable to ```WHERE``` if it is.
|
38
|
+
The ```table_name``` variable is modified to include a trailing dot if it is not blank.
|
39
|
+
The method then checks if the ```occupation``` parameter is blank or equal to ```All``` and returns an empty string in those cases.
|
40
|
+
If the ```occupation``` parameter is ```Military```, the method returns a SQL clause string with the ```occupation``` field equal to ```Military```.
|
41
|
+
If the ```occupation``` parameter is ```Civilian```, the method returns a SQL clause string with the ```occupation``` field not equal to ```Military```.
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
The ``external_client_query`` method quotes the ```end_date``` parameter using ActiveRecord::Base.connection.quote.
|
46
|
+
The method then returns a multi-line SQL query string that retrieves person_ids from the ```obs``` table based on certain conditions.
|
47
|
+
|
48
|
+
|
49
|
+
### Outputs
|
50
|
+
The ``occupation_filter`` method returns a SQL clause string based on the occupation value.
|
51
|
+
|
52
|
+
|
53
|
+
The ``external_client_query`` method returns a multi-line SQL query string.
|