his_emr_api_lab 2.1.1 → 2.1.3
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/controllers/lab/labels_controller.rb +2 -1
- data/app/controllers/lab/orders_controller.rb +14 -25
- data/app/controllers/lab/results_controller.rb +2 -1
- data/app/controllers/lab/specimen_types_controller.rb +0 -1
- data/app/controllers/lab/test_methods_controller.rb +9 -0
- data/app/controllers/lab/test_types_controller.rb +1 -1
- data/app/controllers/lab/tests_controller.rb +1 -3
- data/app/controllers/lab/users_controller.rb +2 -1
- data/app/models/lab/lab_result.rb +4 -0
- data/app/models/lab/lab_test.rb +1 -1
- data/app/models/lab/order_extension.rb +14 -0
- data/app/serializers/lab/lab_order_serializer.rb +20 -4
- data/app/serializers/lab/result_serializer.rb +1 -1
- data/app/serializers/lab/test_serializer.rb +24 -1
- data/app/services/lab/acknowledgement_service.rb +4 -1
- data/app/services/lab/concepts_service.rb +77 -22
- data/app/services/lab/lims/acknowledgement_worker.rb +1 -1
- data/app/services/lab/lims/api/rest_api.rb +543 -78
- data/app/services/lab/lims/config.rb +7 -2
- data/app/services/lab/lims/exceptions.rb +18 -6
- data/app/services/lab/lims/migrator.rb +3 -3
- data/app/services/lab/lims/order_dto.rb +1 -1
- data/app/services/lab/lims/order_serializer.rb +28 -7
- data/app/services/lab/lims/push_worker.rb +3 -3
- data/app/services/lab/lims/utils.rb +5 -7
- data/app/services/lab/lims/worker.rb +1 -1
- data/app/services/lab/metadata.rb +3 -0
- data/app/services/lab/notification_service.rb +72 -0
- data/app/services/lab/orders_search_service.rb +11 -5
- data/app/services/lab/orders_service.rb +79 -38
- data/app/services/lab/results_service.rb +30 -16
- data/app/services/lab/tests_service.rb +15 -3
- data/config/routes.rb +1 -0
- data/db/migrate/20260119104240_add_fulfiller_fields_to_orders.rb +11 -0
- data/db/migrate/20260119104241_create_comment_to_fulfiller_concept.rb +50 -0
- data/lib/lab/version.rb +1 -1
- data/lib/mahis_emr_api_lab.rb +6 -0
- metadata +11 -5
- /data/app/services/lab/lims/api/{couchdb_api.rb → couch_db_api.rb} +0 -0
|
@@ -20,8 +20,10 @@ module Lab
|
|
|
20
20
|
serializer = {}
|
|
21
21
|
results_obs = {}
|
|
22
22
|
ActiveRecord::Base.transaction do
|
|
23
|
-
test = Lab::LabTest.find(test_id)
|
|
23
|
+
test = Lab::LabTest.find(test_id) rescue nil
|
|
24
|
+
test = Lab::LabTest.find_by_uuid(test_id) if test.blank?
|
|
24
25
|
encounter = find_encounter(test, encounter_id: params[:encounter_id],
|
|
26
|
+
encounter_uuid: params[:encounter],
|
|
25
27
|
date: params[:date]&.to_date,
|
|
26
28
|
provider_id: params[:provider_id])
|
|
27
29
|
|
|
@@ -33,7 +35,11 @@ module Lab
|
|
|
33
35
|
serializer = Lab::ResultSerializer.serialize(results_obs)
|
|
34
36
|
end
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
# force commit all transactions
|
|
39
|
+
ActiveRecord::Base.connection.commit_db_transaction
|
|
40
|
+
|
|
41
|
+
# delay job by a second
|
|
42
|
+
ProcessLabResultJob.set(wait: 1.second).perform_later(results_obs.id, serializer, result_enter_by)
|
|
37
43
|
|
|
38
44
|
Rails.logger.info("Lab::ResultsService: Result created for test #{test_id} #{serializer}")
|
|
39
45
|
serializer
|
|
@@ -52,10 +58,10 @@ module Lab
|
|
|
52
58
|
Specimen: ConceptName.find_by(concept_id: order.concept_id)&.name,
|
|
53
59
|
'Test type': ConceptName.find_by(concept_id: result.test.value_coded)&.name,
|
|
54
60
|
'Accession number': order&.accession_number,
|
|
55
|
-
'Orde date': order
|
|
61
|
+
'Orde date': Order.columns.include?('start_date') ? order.start_date : order.date_created,
|
|
56
62
|
'ARV-Number': find_arv_number(result.person_id),
|
|
57
63
|
PatientID: result.person_id,
|
|
58
|
-
'Ordered By': order&.provider&.person&.name,
|
|
64
|
+
'Ordered By': Order.columns.include?('provider_id') ? order&.provider&.person&.name : Person.find(order.creator)&.name,
|
|
59
65
|
Result: values }.as_json
|
|
60
66
|
NotificationService.new.create_notification(result_enter_by, data)
|
|
61
67
|
end
|
|
@@ -73,22 +79,28 @@ module Lab
|
|
|
73
79
|
.first&.identifier
|
|
74
80
|
end
|
|
75
81
|
|
|
76
|
-
def find_encounter(test, encounter_id: nil, date: nil, provider_id: nil)
|
|
82
|
+
def find_encounter(test, encounter_id: nil, encounter_uuid: nil, date: nil, provider_id: nil)
|
|
77
83
|
return Encounter.find(encounter_id) if encounter_id
|
|
84
|
+
return Encounter.find_by_uuid(encounter_uuid) if encounter_uuid
|
|
85
|
+
encounter_type = EncounterType.find_by_name!(Lab::Metadata::ENCOUNTER_TYPE_NAME)
|
|
78
86
|
|
|
79
|
-
Encounter.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
encounter = Encounter.new
|
|
88
|
+
encounter.patient_id = test.person_id
|
|
89
|
+
encounter.program_id = test.encounter.program_id if Encounter.column_names.include?('program_id')
|
|
90
|
+
encounter.visit_id = test.encounter.visit_id if Encounter.column_names.include?('visit_id')
|
|
91
|
+
encounter.type = encounter_type
|
|
92
|
+
encounter.encounter_type = encounter_type if (encounter&.encounter_type.nil? || encounter&.type.nil?)
|
|
93
|
+
encounter.encounter_datetime = date || Date.today
|
|
94
|
+
encounter.provider_id = provider_id || User.current.user_id if Encounter.column_names.include?('provider_id')
|
|
95
|
+
|
|
96
|
+
encounter.save!
|
|
97
|
+
|
|
98
|
+
encounter
|
|
86
99
|
end
|
|
87
100
|
|
|
88
101
|
# Creates the parent observation for results to which the different measures are attached
|
|
89
102
|
def create_results_obs(encounter, test, date, comments = nil)
|
|
90
103
|
void_existing_results_obs(encounter, test)
|
|
91
|
-
|
|
92
104
|
Lab::LabResult.create!(
|
|
93
105
|
person_id: encounter.patient_id,
|
|
94
106
|
encounter_id: encounter.encounter_id,
|
|
@@ -118,11 +130,13 @@ module Lab
|
|
|
118
130
|
def add_measure_to_results(results_obs, params, date)
|
|
119
131
|
validate_measure_params(params)
|
|
120
132
|
|
|
133
|
+
concept_id = params[:indicator][:concept_id] || Concept.find_concept_by_uuid(params.dig(:indicator, :concept))&.id
|
|
134
|
+
|
|
121
135
|
Observation.create!(
|
|
122
136
|
person_id: results_obs.person_id,
|
|
123
137
|
encounter_id: results_obs.encounter_id,
|
|
124
138
|
order_id: results_obs.order_id,
|
|
125
|
-
concept_id:
|
|
139
|
+
concept_id: concept_id,
|
|
126
140
|
obs_group_id: results_obs.obs_id,
|
|
127
141
|
obs_datetime: date&.to_datetime || DateTime.now,
|
|
128
142
|
**make_measure_value(params)
|
|
@@ -132,8 +146,8 @@ module Lab
|
|
|
132
146
|
def validate_measure_params(params)
|
|
133
147
|
raise InvalidParameterError, 'measures.value is required' if params[:value].blank?
|
|
134
148
|
|
|
135
|
-
if params[:indicator]&.[](:concept_id).blank?
|
|
136
|
-
raise InvalidParameterError, 'measures.indicator.concept_id is required'
|
|
149
|
+
if params[:indicator]&.[](:concept_id).blank? && params[:indicator]&.[](:concept).blank?
|
|
150
|
+
raise InvalidParameterError, 'measures.indicator.concept_id or concept is required'
|
|
137
151
|
end
|
|
138
152
|
|
|
139
153
|
params
|
|
@@ -7,9 +7,15 @@ module Lab
|
|
|
7
7
|
class << self
|
|
8
8
|
def find_tests(filters)
|
|
9
9
|
tests = Lab::LabTest.all
|
|
10
|
+
patient_id = filters.delete(:patient_id)
|
|
11
|
+
patient_id ||= ::Patient.find(filters.delete(:patient))&.id if filters[:patient]
|
|
12
|
+
|
|
13
|
+
Lab::UpdatePatientOrdersJob.perform_later(patient_id) if patient_id
|
|
10
14
|
|
|
11
15
|
tests = filter_tests(tests, test_type_id: filters.delete(:test_type_id),
|
|
12
|
-
patient_id: filters.delete(:patient_id)
|
|
16
|
+
patient_id: filters.delete(:patient_id),
|
|
17
|
+
patient: filters.delete(:patient)
|
|
18
|
+
)
|
|
13
19
|
|
|
14
20
|
tests = filter_tests_by_results(tests) if %w[1 true].include?(filters[:pending_results]&.downcase)
|
|
15
21
|
|
|
@@ -23,8 +29,12 @@ module Lab
|
|
|
23
29
|
def create_tests(order, date, tests_params)
|
|
24
30
|
raise InvalidParameterError, 'tests are required' if tests_params.nil? || tests_params.empty?
|
|
25
31
|
|
|
32
|
+
|
|
26
33
|
Lab::LabTest.transaction do
|
|
27
34
|
tests_params.map do |params|
|
|
35
|
+
concept_id = params[:concept_id]
|
|
36
|
+
concept_id = Concept.find_concept_by_uuid(params[:concept]).id if concept_id.nil?
|
|
37
|
+
|
|
28
38
|
test = Lab::LabTest.create!(
|
|
29
39
|
concept_id: ConceptName.find_by_name!(Lab::Metadata::TEST_TYPE_CONCEPT_NAME)
|
|
30
40
|
.concept_id,
|
|
@@ -32,7 +42,7 @@ module Lab
|
|
|
32
42
|
order_id: order.order_id,
|
|
33
43
|
person_id: order.patient_id,
|
|
34
44
|
obs_datetime: date&.to_time || Time.now,
|
|
35
|
-
value_coded:
|
|
45
|
+
value_coded: concept_id
|
|
36
46
|
)
|
|
37
47
|
|
|
38
48
|
Lab::TestSerializer.serialize(test, order:)
|
|
@@ -44,9 +54,11 @@ module Lab
|
|
|
44
54
|
|
|
45
55
|
##
|
|
46
56
|
# Filter a LabTests Relation.
|
|
47
|
-
def filter_tests(tests, test_type_id: nil, patient_id: nil)
|
|
57
|
+
def filter_tests(tests, test_type_id: nil, patient_id: nil, patient: nil)
|
|
48
58
|
tests = tests.where(value_coded: test_type_id) if test_type_id
|
|
49
59
|
tests = tests.where(person_id: patient_id) if patient_id
|
|
60
|
+
person = ::Person.find_by_uuid(patient) if patient
|
|
61
|
+
tests = tests.where(person_id: person&.patient&.id) if patient
|
|
50
62
|
|
|
51
63
|
tests
|
|
52
64
|
end
|
data/config/routes.rb
CHANGED
|
@@ -14,6 +14,7 @@ Lab::Engine.routes.draw do
|
|
|
14
14
|
|
|
15
15
|
get 'api/v1/lab/labels/order', to: 'labels#print_order_label'
|
|
16
16
|
get 'api/v1/lab/accession_number', to: 'orders#verify_tracking_number'
|
|
17
|
+
get 'api/v1/lab/test_methods', to: 'test_methods#index'
|
|
17
18
|
|
|
18
19
|
# Metadata
|
|
19
20
|
# TODO: Move the following to namespace /concepts
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This migration adds fields to the orders table to support the comment to fulfiller feature.
|
|
4
|
+
class AddFulfillerFieldsToOrders < ActiveRecord::Migration[5.2]
|
|
5
|
+
def change
|
|
6
|
+
add_column :orders, :comment_to_fulfiller, :string, limit: 1024 unless column_exists?(:orders,
|
|
7
|
+
:comment_to_fulfiller)
|
|
8
|
+
add_column :orders, :fulfiller_comment, :string, limit: 1024 unless column_exists?(:orders, :fulfiller_comment)
|
|
9
|
+
add_column :orders, :fulfiller_status, :string, limit: 50 unless column_exists?(:orders, :fulfiller_status)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This migration creates the concept for the comment to fulfiller feature.
|
|
4
|
+
class CreateCommentToFulfillerConcept < ActiveRecord::Migration[5.2]
|
|
5
|
+
def up
|
|
6
|
+
# Return if the concept already exists
|
|
7
|
+
return if ConceptName.exists?(name: 'Comment to fulfiller')
|
|
8
|
+
|
|
9
|
+
ActiveRecord::Base.transaction do
|
|
10
|
+
# Find or create the concept class for Misc
|
|
11
|
+
concept_class = ConceptClass.find_by(name: 'Finding')
|
|
12
|
+
|
|
13
|
+
# Find or create the concept datatype for Text
|
|
14
|
+
concept_datatype = ConceptDatatype.find_by(name: 'Text')
|
|
15
|
+
|
|
16
|
+
# Create the concept
|
|
17
|
+
concept = Concept.create!(
|
|
18
|
+
class_id: concept_class.id,
|
|
19
|
+
datatype_id: concept_datatype.id,
|
|
20
|
+
short_name: 'Comment to fulfiller',
|
|
21
|
+
retired: 0,
|
|
22
|
+
is_set: 0,
|
|
23
|
+
creator: 1,
|
|
24
|
+
date_created: Time.current,
|
|
25
|
+
uuid: SecureRandom.uuid
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Create the concept name
|
|
29
|
+
ConceptName.create!(
|
|
30
|
+
concept: concept,
|
|
31
|
+
name: 'Comment to fulfiller',
|
|
32
|
+
locale: 'en',
|
|
33
|
+
locale_preferred: 1,
|
|
34
|
+
concept_name_type: 'FULLY_SPECIFIED',
|
|
35
|
+
creator: 1,
|
|
36
|
+
date_created: Time.current,
|
|
37
|
+
uuid: SecureRandom.uuid
|
|
38
|
+
)
|
|
39
|
+
puts "Created 'Comment to fulfiller' concept with ID: #{concept.concept_id}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def down
|
|
44
|
+
concept = ConceptName.find_by(name: 'Comment to fulfiller')&.concept
|
|
45
|
+
return unless concept
|
|
46
|
+
|
|
47
|
+
ConceptName.where(concept: concept).destroy_all
|
|
48
|
+
concept.destroy
|
|
49
|
+
end
|
|
50
|
+
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.3
|
|
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-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: couchrest
|
|
@@ -42,14 +42,14 @@ dependencies:
|
|
|
42
42
|
name: rails
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
|
-
- - "
|
|
45
|
+
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
47
|
version: 7.0.6
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
|
-
- - "
|
|
52
|
+
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: 7.0.6
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
@@ -238,6 +238,7 @@ files:
|
|
|
238
238
|
- app/controllers/lab/reasons_for_test_controller.rb
|
|
239
239
|
- app/controllers/lab/results_controller.rb
|
|
240
240
|
- app/controllers/lab/specimen_types_controller.rb
|
|
241
|
+
- app/controllers/lab/test_methods_controller.rb
|
|
241
242
|
- app/controllers/lab/test_result_indicators_controller.rb
|
|
242
243
|
- app/controllers/lab/test_types_controller.rb
|
|
243
244
|
- app/controllers/lab/tests_controller.rb
|
|
@@ -257,6 +258,7 @@ files:
|
|
|
257
258
|
- app/models/lab/lab_test.rb
|
|
258
259
|
- app/models/lab/lims_failed_import.rb
|
|
259
260
|
- app/models/lab/lims_order_mapping.rb
|
|
261
|
+
- app/models/lab/order_extension.rb
|
|
260
262
|
- app/serializers/lab/lab_order_serializer.rb
|
|
261
263
|
- app/serializers/lab/result_serializer.rb
|
|
262
264
|
- app/serializers/lab/test_serializer.rb
|
|
@@ -268,7 +270,7 @@ files:
|
|
|
268
270
|
- app/services/lab/lims/acknowledgement_serializer.rb
|
|
269
271
|
- app/services/lab/lims/acknowledgement_worker.rb
|
|
270
272
|
- app/services/lab/lims/api/blackhole_api.rb
|
|
271
|
-
- app/services/lab/lims/api/
|
|
273
|
+
- app/services/lab/lims/api/couch_db_api.rb
|
|
272
274
|
- app/services/lab/lims/api/mysql_api.rb
|
|
273
275
|
- app/services/lab/lims/api/rest_api.rb
|
|
274
276
|
- app/services/lab/lims/api/ws_api.rb
|
|
@@ -283,6 +285,7 @@ files:
|
|
|
283
285
|
- app/services/lab/lims/utils.rb
|
|
284
286
|
- app/services/lab/lims/worker.rb
|
|
285
287
|
- app/services/lab/metadata.rb
|
|
288
|
+
- app/services/lab/notification_service.rb
|
|
286
289
|
- app/services/lab/orders_search_service.rb
|
|
287
290
|
- app/services/lab/orders_service.rb
|
|
288
291
|
- app/services/lab/results_service.rb
|
|
@@ -296,6 +299,8 @@ files:
|
|
|
296
299
|
- db/migrate/20210407071728_create_lab_lims_failed_imports.rb
|
|
297
300
|
- db/migrate/20210610095024_fix_numeric_results_value_type.rb
|
|
298
301
|
- db/migrate/20210807111531_add_default_to_lims_order_mapping.rb
|
|
302
|
+
- db/migrate/20260119104240_add_fulfiller_fields_to_orders.rb
|
|
303
|
+
- db/migrate/20260119104241_create_comment_to_fulfiller_concept.rb
|
|
299
304
|
- lib/auto12epl.rb
|
|
300
305
|
- lib/couch_bum/couch_bum.rb
|
|
301
306
|
- lib/generators/lab/install/USAGE
|
|
@@ -307,6 +312,7 @@ files:
|
|
|
307
312
|
- lib/lab/engine.rb
|
|
308
313
|
- lib/lab/version.rb
|
|
309
314
|
- lib/logger_multiplexor.rb
|
|
315
|
+
- lib/mahis_emr_api_lab.rb
|
|
310
316
|
- lib/tasks/lab_tasks.rake
|
|
311
317
|
- lib/tasks/loaders/data/reasons-for-test.csv
|
|
312
318
|
- lib/tasks/loaders/data/test-measures.csv
|
|
File without changes
|