his_emr_api_lab 2.1.3 → 2.1.4.pre.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/jobs/lab/process_lab_result_job.rb +1 -0
- data/app/serializers/lab/lab_order_serializer.rb +1 -1
- data/app/serializers/lab/result_serializer.rb +12 -6
- data/app/services/lab/concepts_service.rb +3 -3
- data/app/services/lab/lims/api/rest_api.rb +1 -1
- data/app/services/lab/lims/migrator.rb +1 -1
- data/app/services/lab/lims/order_serializer.rb +4 -9
- data/app/services/lab/lims/pull_worker.rb +1 -1
- data/app/services/lab/lims/push_worker.rb +1 -1
- data/app/services/lab/lims/utils.rb +6 -2
- data/app/services/lab/orders_service.rb +1 -1
- data/app/services/lab/results_service.rb +27 -13
- data/db/migrate/20260128111557_add_program_id_to_encounter.rb +13 -0
- data/lib/lab/version.rb +1 -1
- metadata +6 -5
- /data/app/services/lab/lims/api/{couch_db_api.rb → couchdb_api.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 306b46064eb5c58668eb1f4598d99258f1e76b69db42b90abe33d35ddd2434b0
|
|
4
|
+
data.tar.gz: f367e1fdf5101dd25a2e64f83f61ac6eeffe4fee2007846da5037c18083d1b96
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a8e09ab538d005a20b43f41b0c47357c0440a8cb8508f223b4d3f9639c1b3df2dc87a3c6f33d3c6cecd5604c77a1b4cb36f14e335e0df52732280ca1256c41b6
|
|
7
|
+
data.tar.gz: 47fdae979daa748c4dbc6fd1c291594b857a40b45e4f3adea3ecaae8fcb54275c2c77485e8bf2f2c222eddea66a1518f3c396236dbd56f4b4afcd9b16bfd4e76
|
|
@@ -6,6 +6,7 @@ module Lab
|
|
|
6
6
|
class ProcessLabResultJob < ApplicationJob
|
|
7
7
|
queue_as :default
|
|
8
8
|
def perform(results_obs_id, serializer, result_enter_by)
|
|
9
|
+
Rails.logger.info("Lab::ProcessLabResultJob: Processing result completion for #{serializer}")
|
|
9
10
|
results_obs = Lab::LabResult.find(results_obs_id)
|
|
10
11
|
Lab::ResultsService.process_result_completion(results_obs, serializer, result_enter_by)
|
|
11
12
|
end
|
|
@@ -21,7 +21,7 @@ module Lab
|
|
|
21
21
|
order_date: order.date_created,
|
|
22
22
|
location_id: encounter.location_id,
|
|
23
23
|
program_id: encounter.program_id,
|
|
24
|
-
program_name: program
|
|
24
|
+
program_name: program&.name,
|
|
25
25
|
# order_date: order.start_date,
|
|
26
26
|
patient_id: order.patient_id,
|
|
27
27
|
accession_number: order.accession_number,
|
|
@@ -7,11 +7,11 @@ module Lab
|
|
|
7
7
|
def self.serialize(result)
|
|
8
8
|
result.children.map do |measure|
|
|
9
9
|
value, value_type = read_value(measure)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
program_id =
|
|
10
|
+
# Get the test catalog name instead of any random concept name
|
|
11
|
+
concept_name = get_test_catalog_concept_name(measure.concept_id)
|
|
12
|
+
program_id = ''
|
|
13
13
|
if measure.obs_id.present?
|
|
14
|
-
obs = Observation.find(measure.obs_id)
|
|
14
|
+
obs = Observation.unscope(where: :obs_group_id).find(measure.obs_id)
|
|
15
15
|
encounter = Encounter.find(obs.encounter_id)
|
|
16
16
|
program_id = encounter.program_id
|
|
17
17
|
end
|
|
@@ -19,8 +19,8 @@ module Lab
|
|
|
19
19
|
{
|
|
20
20
|
id: measure.obs_id,
|
|
21
21
|
indicator: {
|
|
22
|
-
concept_id:
|
|
23
|
-
name: concept_name
|
|
22
|
+
concept_id: measure.concept_id,
|
|
23
|
+
name: concept_name
|
|
24
24
|
},
|
|
25
25
|
date: measure.obs_datetime,
|
|
26
26
|
value:,
|
|
@@ -31,6 +31,12 @@ module Lab
|
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
def self.get_test_catalog_concept_name(concept_id)
|
|
35
|
+
return nil unless concept_id
|
|
36
|
+
|
|
37
|
+
::ConceptAttribute.find_by(concept_id:, attribute_type: ConceptAttributeType.test_catalogue_name)&.value_reference
|
|
38
|
+
end
|
|
39
|
+
|
|
34
40
|
def self.read_value(measure)
|
|
35
41
|
%w[value_numeric value_coded value_boolean value_text].each do |field|
|
|
36
42
|
value = measure.send(field) if measure.respond_to?(field)
|
|
@@ -9,7 +9,7 @@ module Lab
|
|
|
9
9
|
FROM concept_set cs
|
|
10
10
|
INNER JOIN concept_name cn ON cn.concept_id = cs.concept_id
|
|
11
11
|
INNER JOIN concept_attribute ca ON ca.value_reference = #{ActiveRecord::Base.connection.quote(nlims_code)}
|
|
12
|
-
AND ca.attribute_type_id =
|
|
12
|
+
AND ca.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
|
|
13
13
|
WHERE cs.concept_id IN (SELECT concept_set.concept_id
|
|
14
14
|
FROM concept_set
|
|
15
15
|
WHERE concept_set.concept_set IN (SELECT concept_name.concept_id
|
|
@@ -22,8 +22,8 @@ module Lab
|
|
|
22
22
|
FROM concept_name
|
|
23
23
|
WHERE concept_name.voided = 0
|
|
24
24
|
AND concept_name.name = 'Test type')
|
|
25
|
-
AND concept_set.concept_id = ca.concept_id
|
|
26
25
|
)
|
|
26
|
+
AND cs.concept_set = ca.concept_id
|
|
27
27
|
GROUP BY cn.concept_id
|
|
28
28
|
SQL
|
|
29
29
|
end
|
|
@@ -61,7 +61,7 @@ module Lab
|
|
|
61
61
|
INNER JOIN concept_attribute ca2 ON ca.concept_id = ca2.concept_id
|
|
62
62
|
AND ca2.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
|
|
63
63
|
WHERE ca.attribute_type_id = #{ConceptAttributeType.test_catalogue_name.concept_attribute_type_id}
|
|
64
|
-
AND ca.concept_id IN (#{concept_set.select(:
|
|
64
|
+
AND ca.concept_id IN (#{concept_set.select(:concept_set).to_sql})
|
|
65
65
|
GROUP BY ca.concept_id
|
|
66
66
|
SQL
|
|
67
67
|
end
|
|
@@ -341,7 +341,7 @@ module Lab
|
|
|
341
341
|
def nlims_order_exists?(tracking_number)
|
|
342
342
|
response = in_authenticated_session do |headers|
|
|
343
343
|
Rails.logger.info("Verifying order ##{tracking_number}")
|
|
344
|
-
RestClient.get(expand_uri("orders/#{tracking_number}", api_version: 'v2'), headers)
|
|
344
|
+
RestClient.get(expand_uri("orders/#{tracking_number}/exists", api_version: 'v2'), headers)
|
|
345
345
|
end
|
|
346
346
|
|
|
347
347
|
Rails.logger.info("Order ##{tracking_number} verified... Parsing...")
|
|
@@ -29,7 +29,7 @@ require 'lab/lab_test'
|
|
|
29
29
|
require 'lab/lims_order_mapping'
|
|
30
30
|
require 'lab/lims_failed_import'
|
|
31
31
|
|
|
32
|
-
require_relative 'api/
|
|
32
|
+
require_relative 'api/couchdb_api'
|
|
33
33
|
require_relative 'config'
|
|
34
34
|
require_relative 'pull_worker'
|
|
35
35
|
require_relative 'utils'
|
|
@@ -144,10 +144,9 @@ module Lab
|
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
def format_test_status_trail(order)
|
|
147
|
-
tests = order.voided ? order.tests : Lab::LabOrderSerializer.voided_tests(order)
|
|
148
|
-
|
|
147
|
+
tests = [0, false].include?(order.voided) ? order.tests : Lab::LabOrderSerializer.voided_tests(order)
|
|
149
148
|
tests.each_with_object({}) do |test, trail|
|
|
150
|
-
test_name = format_test_name(
|
|
149
|
+
test_name = format_test_name(::Concept.find(test.value_coded).test_catalogue_name)
|
|
151
150
|
|
|
152
151
|
current_test_trail = trail[test_name] = {}
|
|
153
152
|
|
|
@@ -156,7 +155,7 @@ module Lab
|
|
|
156
155
|
updated_by: find_user(test.creator)
|
|
157
156
|
}
|
|
158
157
|
|
|
159
|
-
unless test.voided
|
|
158
|
+
unless [0, false].include?(test.voided)
|
|
160
159
|
current_test_trail[test.date_voided.strftime('%Y%m%d%H%M%S')] = {
|
|
161
160
|
status: 'Voided',
|
|
162
161
|
updated_by: find_user(test.voided_by)
|
|
@@ -209,11 +208,7 @@ module Lab
|
|
|
209
208
|
end
|
|
210
209
|
|
|
211
210
|
def format_test_name(test_name)
|
|
212
|
-
return
|
|
213
|
-
|
|
214
|
-
return 'TB' if test_name.casecmp?('TB Program')
|
|
215
|
-
|
|
216
|
-
test_name.titleize
|
|
211
|
+
return test_name
|
|
217
212
|
end
|
|
218
213
|
|
|
219
214
|
def format_sample_priority(priority)
|
|
@@ -51,7 +51,7 @@ module Lab
|
|
|
51
51
|
mapping = Lab::LimsOrderMapping.find_by(order_id: order.order_id)
|
|
52
52
|
|
|
53
53
|
ActiveRecord::Base.transaction do
|
|
54
|
-
if mapping && !order.voided
|
|
54
|
+
if mapping && ![0, false].include?(order.voided)
|
|
55
55
|
Rails.logger.info("Deleting order ##{order_dto['accession_number']} from LIMS")
|
|
56
56
|
lims_api.delete_order(mapping.lims_id, order_dto)
|
|
57
57
|
mapping.destroy
|
|
@@ -78,8 +78,12 @@ module Lab
|
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def self.find_concept_by_name(name)
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
unescaped_name = CGI.unescapeHTML(name)
|
|
82
|
+
attribute_type_id = ConceptAttributeType.test_catalogue_name.concept_attribute_type_id
|
|
83
|
+
|
|
84
|
+
ConceptName.joins('INNER JOIN concept_attribute ON concept_attribute.concept_id = concept_name.concept_id')
|
|
85
|
+
.where('concept_attribute.attribute_type_id = ? AND concept_attribute.value_reference = ? AND concept_name.name = ?',
|
|
86
|
+
attribute_type_id, unescaped_name, unescaped_name)
|
|
83
87
|
.first
|
|
84
88
|
end
|
|
85
89
|
end
|
|
@@ -234,7 +234,7 @@ module Lab
|
|
|
234
234
|
encounter.encounter_type = EncounterType.find_by_name!(Lab::Metadata::ENCOUNTER_TYPE_NAME)
|
|
235
235
|
encounter.encounter_datetime = order_params[:date] || Date.today
|
|
236
236
|
encounter.visit = Visit.find_by_uuid(visit) if Encounter.column_names.include?('visit_id')
|
|
237
|
-
encounter.provider_id = User.current&.person
|
|
237
|
+
encounter.provider_id = User.current&.person&.id if Encounter.column_names.include?('provider_id')
|
|
238
238
|
encounter.program_id = order_params[:program_id] if Encounter.column_names.include?('program_id') && order_params[:program_id].present?
|
|
239
239
|
encounter.save!
|
|
240
240
|
encounter.reload
|
|
@@ -20,7 +20,11 @@ module Lab
|
|
|
20
20
|
serializer = {}
|
|
21
21
|
results_obs = {}
|
|
22
22
|
ActiveRecord::Base.transaction do
|
|
23
|
-
test =
|
|
23
|
+
test = begin
|
|
24
|
+
Lab::LabTest.find(test_id)
|
|
25
|
+
rescue StandardError
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
24
28
|
test = Lab::LabTest.find_by_uuid(test_id) if test.blank?
|
|
25
29
|
encounter = find_encounter(test, encounter_id: params[:encounter_id],
|
|
26
30
|
encounter_uuid: params[:encounter],
|
|
@@ -37,9 +41,9 @@ module Lab
|
|
|
37
41
|
|
|
38
42
|
# force commit all transactions
|
|
39
43
|
ActiveRecord::Base.connection.commit_db_transaction
|
|
40
|
-
|
|
41
|
-
#
|
|
42
|
-
ProcessLabResultJob.
|
|
44
|
+
|
|
45
|
+
# Execute job synchronously
|
|
46
|
+
ProcessLabResultJob.perform_now(results_obs.id, serializer, result_enter_by)
|
|
43
47
|
|
|
44
48
|
Rails.logger.info("Lab::ResultsService: Result created for test #{test_id} #{serializer}")
|
|
45
49
|
serializer
|
|
@@ -55,10 +59,10 @@ module Lab
|
|
|
55
59
|
def precess_notification_message(result, values, result_enter_by)
|
|
56
60
|
order = Order.find(result.order_id)
|
|
57
61
|
data = { Type: result_enter_by,
|
|
58
|
-
Specimen: ConceptName.find_by(concept_id: order.concept_id)&.name,
|
|
59
|
-
'Test type': ConceptName.find_by(concept_id: result.test.value_coded)&.name,
|
|
62
|
+
Specimen: get_test_catalog_name(order.concept_id) || ConceptName.find_by(concept_id: order.concept_id)&.name,
|
|
63
|
+
'Test type': get_test_catalog_name(result.test.value_coded) || ConceptName.find_by(concept_id: result.test.value_coded)&.name,
|
|
60
64
|
'Accession number': order&.accession_number,
|
|
61
|
-
|
|
65
|
+
order_date: Order.columns.include?('start_date') ? order.start_date : order.date_created,
|
|
62
66
|
'ARV-Number': find_arv_number(result.person_id),
|
|
63
67
|
PatientID: result.person_id,
|
|
64
68
|
'Ordered By': Order.columns.include?('provider_id') ? order&.provider&.person&.name : Person.find(order.creator)&.name,
|
|
@@ -82,19 +86,21 @@ module Lab
|
|
|
82
86
|
def find_encounter(test, encounter_id: nil, encounter_uuid: nil, date: nil, provider_id: nil)
|
|
83
87
|
return Encounter.find(encounter_id) if encounter_id
|
|
84
88
|
return Encounter.find_by_uuid(encounter_uuid) if encounter_uuid
|
|
85
|
-
|
|
89
|
+
|
|
90
|
+
lab_encounter_type = EncounterType.find_by_name!(Lab::Metadata::ENCOUNTER_TYPE_NAME)
|
|
86
91
|
|
|
87
92
|
encounter = Encounter.new
|
|
88
93
|
encounter.patient_id = test.person_id
|
|
89
94
|
encounter.program_id = test.encounter.program_id if Encounter.column_names.include?('program_id')
|
|
90
95
|
encounter.visit_id = test.encounter.visit_id if Encounter.column_names.include?('visit_id')
|
|
91
|
-
|
|
92
|
-
|
|
96
|
+
# Use bracket notation to set the encounter_type column directly (bypasses association)
|
|
97
|
+
# This handles both Integer and EncounterType object
|
|
98
|
+
encounter_type_value = lab_encounter_type.is_a?(Integer) ? lab_encounter_type : lab_encounter_type.encounter_type_id
|
|
99
|
+
encounter[:encounter_type] = encounter_type_value
|
|
93
100
|
encounter.encounter_datetime = date || Date.today
|
|
94
101
|
encounter.provider_id = provider_id || User.current.user_id if Encounter.column_names.include?('provider_id')
|
|
95
|
-
|
|
96
102
|
encounter.save!
|
|
97
|
-
|
|
103
|
+
encounter.reload
|
|
98
104
|
encounter
|
|
99
105
|
end
|
|
100
106
|
|
|
@@ -130,7 +136,8 @@ module Lab
|
|
|
130
136
|
def add_measure_to_results(results_obs, params, date)
|
|
131
137
|
validate_measure_params(params)
|
|
132
138
|
|
|
133
|
-
concept_id = params[:indicator][:concept_id] || Concept.find_concept_by_uuid(params.dig(:indicator,
|
|
139
|
+
concept_id = params[:indicator][:concept_id] || Concept.find_concept_by_uuid(params.dig(:indicator,
|
|
140
|
+
:concept))&.id
|
|
134
141
|
|
|
135
142
|
Observation.create!(
|
|
136
143
|
person_id: results_obs.person_id,
|
|
@@ -174,6 +181,13 @@ module Lab
|
|
|
174
181
|
else raise InvalidParameterError, "Invalid boolean value: #{string}"
|
|
175
182
|
end
|
|
176
183
|
end
|
|
184
|
+
|
|
185
|
+
def get_test_catalog_name(concept_id)
|
|
186
|
+
return nil unless concept_id
|
|
187
|
+
|
|
188
|
+
::ConceptAttribute.find_by(concept_id:,
|
|
189
|
+
attribute_type: ConceptAttributeType.test_catalogue_name)&.value_reference
|
|
190
|
+
end
|
|
177
191
|
end
|
|
178
192
|
end
|
|
179
193
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This migration adds the program_id column to the encounter table if it doesn't already exist.
|
|
4
|
+
# The program_id column is used to associate encounters with specific programs (e.g., HIV Program, TB Program).
|
|
5
|
+
class AddProgramIdToEncounter < ActiveRecord::Migration[5.2]
|
|
6
|
+
def change
|
|
7
|
+
return if column_exists?(:encounter, :program_id)
|
|
8
|
+
|
|
9
|
+
add_column :encounter, :program_id, :integer, after: :encounter_type
|
|
10
|
+
add_index :encounter, :program_id
|
|
11
|
+
add_foreign_key :encounter, :program, column: :program_id, primary_key: :program_id
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/lab/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: his_emr_api_lab
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.
|
|
4
|
+
version: 2.1.4.pre.alpha
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Elizabeth Glaser Pediatric Foundation Malawi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: couchrest
|
|
@@ -270,7 +270,7 @@ files:
|
|
|
270
270
|
- app/services/lab/lims/acknowledgement_serializer.rb
|
|
271
271
|
- app/services/lab/lims/acknowledgement_worker.rb
|
|
272
272
|
- app/services/lab/lims/api/blackhole_api.rb
|
|
273
|
-
- app/services/lab/lims/api/
|
|
273
|
+
- app/services/lab/lims/api/couchdb_api.rb
|
|
274
274
|
- app/services/lab/lims/api/mysql_api.rb
|
|
275
275
|
- app/services/lab/lims/api/rest_api.rb
|
|
276
276
|
- app/services/lab/lims/api/ws_api.rb
|
|
@@ -301,6 +301,7 @@ files:
|
|
|
301
301
|
- db/migrate/20210807111531_add_default_to_lims_order_mapping.rb
|
|
302
302
|
- db/migrate/20260119104240_add_fulfiller_fields_to_orders.rb
|
|
303
303
|
- db/migrate/20260119104241_create_comment_to_fulfiller_concept.rb
|
|
304
|
+
- db/migrate/20260128111557_add_program_id_to_encounter.rb
|
|
304
305
|
- lib/auto12epl.rb
|
|
305
306
|
- lib/couch_bum/couch_bum.rb
|
|
306
307
|
- lib/generators/lab/install/USAGE
|
|
@@ -339,9 +340,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
339
340
|
version: '0'
|
|
340
341
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
341
342
|
requirements:
|
|
342
|
-
- - "
|
|
343
|
+
- - ">"
|
|
343
344
|
- !ruby/object:Gem::Version
|
|
344
|
-
version:
|
|
345
|
+
version: 1.3.1
|
|
345
346
|
requirements: []
|
|
346
347
|
rubygems_version: 3.4.1
|
|
347
348
|
signing_key:
|
|
File without changes
|