his_emr_api_lab 2.1.0 → 2.1.2

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/lab/labels_controller.rb +2 -1
  3. data/app/controllers/lab/orders_controller.rb +14 -25
  4. data/app/controllers/lab/results_controller.rb +2 -1
  5. data/app/controllers/lab/specimen_types_controller.rb +0 -1
  6. data/app/controllers/lab/test_methods_controller.rb +9 -0
  7. data/app/controllers/lab/test_types_controller.rb +1 -1
  8. data/app/controllers/lab/tests_controller.rb +1 -3
  9. data/app/controllers/lab/users_controller.rb +2 -1
  10. data/app/jobs/lab/process_lab_result_job.rb +13 -0
  11. data/app/models/lab/lab_order.rb +4 -1
  12. data/app/models/lab/lab_result.rb +4 -0
  13. data/app/models/lab/lab_test.rb +1 -1
  14. data/app/models/lab/order_extension.rb +14 -0
  15. data/app/serializers/lab/lab_order_serializer.rb +27 -4
  16. data/app/serializers/lab/result_serializer.rb +10 -2
  17. data/app/serializers/lab/test_serializer.rb +24 -1
  18. data/app/services/lab/accession_number_service.rb +2 -2
  19. data/app/services/lab/acknowledgement_service.rb +4 -1
  20. data/app/services/lab/concepts_service.rb +77 -22
  21. data/app/services/lab/lims/acknowledgement_worker.rb +1 -1
  22. data/app/services/lab/lims/api/rest_api.rb +543 -78
  23. data/app/services/lab/lims/config.rb +7 -2
  24. data/app/services/lab/lims/exceptions.rb +7 -6
  25. data/app/services/lab/lims/migrator.rb +3 -3
  26. data/app/services/lab/lims/order_dto.rb +1 -1
  27. data/app/services/lab/lims/order_serializer.rb +28 -7
  28. data/app/services/lab/lims/pull_worker.rb +6 -6
  29. data/app/services/lab/lims/push_worker.rb +3 -3
  30. data/app/services/lab/lims/utils.rb +3 -4
  31. data/app/services/lab/lims/worker.rb +2 -2
  32. data/app/services/lab/metadata.rb +4 -0
  33. data/app/services/lab/notification_service.rb +72 -0
  34. data/app/services/lab/orders_search_service.rb +11 -5
  35. data/app/services/lab/orders_service.rb +82 -36
  36. data/app/services/lab/results_service.rb +32 -17
  37. data/app/services/lab/tests_service.rb +15 -3
  38. data/config/routes.rb +1 -0
  39. data/db/migrate/20260119104240_add_fulfiller_fields_to_orders.rb +11 -0
  40. data/db/migrate/20260119104241_create_comment_to_fulfiller_concept.rb +50 -0
  41. data/lib/lab/version.rb +1 -1
  42. data/lib/mahis_emr_api_lab.rb +6 -0
  43. metadata +12 -5
  44. /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
 
@@ -32,23 +34,30 @@ module Lab
32
34
 
33
35
  serializer = Lab::ResultSerializer.serialize(results_obs)
34
36
  end
35
- process_acknowledgement(results_obs, result_enter_by)
36
- precess_notification_message(results_obs, serializer, result_enter_by)
37
+
38
+ ProcessLabResultJob.perform_later(results_obs.id, serializer, result_enter_by)
39
+
37
40
  Rails.logger.info("Lab::ResultsService: Result created for test #{test_id} #{serializer}")
38
41
  serializer
39
42
  end
40
43
 
44
+ def process_result_completion(results_obs, serializer, result_enter_by)
45
+ process_acknowledgement(results_obs, result_enter_by)
46
+ precess_notification_message(results_obs, serializer, result_enter_by)
47
+ end
48
+
41
49
  private
42
50
 
43
51
  def precess_notification_message(result, values, result_enter_by)
44
52
  order = Order.find(result.order_id)
45
53
  data = { Type: result_enter_by,
54
+ Specimen: ConceptName.find_by(concept_id: order.concept_id)&.name,
46
55
  'Test type': ConceptName.find_by(concept_id: result.test.value_coded)&.name,
47
56
  'Accession number': order&.accession_number,
48
- 'Orde date': order&.start_date,
57
+ 'Orde date': Order.columns.include?('start_date') ? order.start_date : order.date_created,
49
58
  'ARV-Number': find_arv_number(result.person_id),
50
59
  PatientID: result.person_id,
51
- 'Ordered By': order&.provider&.person&.name,
60
+ 'Ordered By': Order.columns.include?('provider_id') ? order&.provider&.person&.name : Person.find(order.creator)&.name,
52
61
  Result: values }.as_json
53
62
  NotificationService.new.create_notification(result_enter_by, data)
54
63
  end
@@ -66,22 +75,26 @@ module Lab
66
75
  .first&.identifier
67
76
  end
68
77
 
69
- def find_encounter(test, encounter_id: nil, date: nil, provider_id: nil)
78
+ def find_encounter(test, encounter_id: nil, encounter_uuid: nil, date: nil, provider_id: nil)
70
79
  return Encounter.find(encounter_id) if encounter_id
80
+ return Encounter.find_by_uuid(encounter_uuid) if encounter_uuid
71
81
 
72
- Encounter.create!(
73
- patient_id: test.person_id,
74
- program_id: test.encounter.program_id,
75
- type: EncounterType.find_by_name!(Lab::Metadata::ENCOUNTER_TYPE_NAME),
76
- encounter_datetime: date || Date.today,
77
- provider_id: provider_id || User.current.user_id
78
- )
82
+ encounter = Encounter.new
83
+ encounter.patient_id = test.person_id
84
+ encounter.program_id = test.encounter.program_id if Encounter.column_names.include?('program_id')
85
+ encounter.visit_id = test.encounter.visit_id if Encounter.column_names.include?('visit_id')
86
+ encounter.encounter_type = EncounterType.find_by_name!(Lab::Metadata::ENCOUNTER_TYPE_NAME)
87
+ encounter.encounter_datetime = date || Date.today
88
+ encounter.provider_id = provider_id || User.current.user_id if Encounter.column_names.include?('provider_id')
89
+
90
+ encounter.save!
91
+
92
+ encounter
79
93
  end
80
94
 
81
95
  # Creates the parent observation for results to which the different measures are attached
82
96
  def create_results_obs(encounter, test, date, comments = nil)
83
97
  void_existing_results_obs(encounter, test)
84
-
85
98
  Lab::LabResult.create!(
86
99
  person_id: encounter.patient_id,
87
100
  encounter_id: encounter.encounter_id,
@@ -111,11 +124,13 @@ module Lab
111
124
  def add_measure_to_results(results_obs, params, date)
112
125
  validate_measure_params(params)
113
126
 
127
+ concept_id = params[:indicator][:concept_id] || Concept.find_concept_by_uuid(params.dig(:indicator, :concept))&.id
128
+
114
129
  Observation.create!(
115
130
  person_id: results_obs.person_id,
116
131
  encounter_id: results_obs.encounter_id,
117
132
  order_id: results_obs.order_id,
118
- concept_id: params[:indicator][:concept_id],
133
+ concept_id: concept_id,
119
134
  obs_group_id: results_obs.obs_id,
120
135
  obs_datetime: date&.to_datetime || DateTime.now,
121
136
  **make_measure_value(params)
@@ -125,8 +140,8 @@ module Lab
125
140
  def validate_measure_params(params)
126
141
  raise InvalidParameterError, 'measures.value is required' if params[:value].blank?
127
142
 
128
- if params[:indicator]&.[](:concept_id).blank?
129
- raise InvalidParameterError, 'measures.indicator.concept_id is required'
143
+ if params[:indicator]&.[](:concept_id).blank? && params[:indicator]&.[](:concept).blank?
144
+ raise InvalidParameterError, 'measures.indicator.concept_id or concept is required'
130
145
  end
131
146
 
132
147
  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: params[:concept_id]
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lab
4
- VERSION = '2.1.0'
4
+ VERSION = '2.1.2'
5
5
  end
@@ -0,0 +1,6 @@
1
+ require 'lab/engine'
2
+ require 'lab/version'
3
+
4
+ module Lab
5
+ # Your code goes here...
6
+ end
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.0
4
+ version: 2.1.2
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-08 00:00:00.000000000 Z
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,11 +238,13 @@ 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
244
245
  - app/controllers/lab/users_controller.rb
245
246
  - app/jobs/lab/application_job.rb
247
+ - app/jobs/lab/process_lab_result_job.rb
246
248
  - app/jobs/lab/push_order_job.rb
247
249
  - app/jobs/lab/update_patient_orders_job.rb
248
250
  - app/jobs/lab/void_order_job.rb
@@ -256,6 +258,7 @@ files:
256
258
  - app/models/lab/lab_test.rb
257
259
  - app/models/lab/lims_failed_import.rb
258
260
  - app/models/lab/lims_order_mapping.rb
261
+ - app/models/lab/order_extension.rb
259
262
  - app/serializers/lab/lab_order_serializer.rb
260
263
  - app/serializers/lab/result_serializer.rb
261
264
  - app/serializers/lab/test_serializer.rb
@@ -267,7 +270,7 @@ files:
267
270
  - app/services/lab/lims/acknowledgement_serializer.rb
268
271
  - app/services/lab/lims/acknowledgement_worker.rb
269
272
  - app/services/lab/lims/api/blackhole_api.rb
270
- - app/services/lab/lims/api/couchdb_api.rb
273
+ - app/services/lab/lims/api/couch_db_api.rb
271
274
  - app/services/lab/lims/api/mysql_api.rb
272
275
  - app/services/lab/lims/api/rest_api.rb
273
276
  - app/services/lab/lims/api/ws_api.rb
@@ -282,6 +285,7 @@ files:
282
285
  - app/services/lab/lims/utils.rb
283
286
  - app/services/lab/lims/worker.rb
284
287
  - app/services/lab/metadata.rb
288
+ - app/services/lab/notification_service.rb
285
289
  - app/services/lab/orders_search_service.rb
286
290
  - app/services/lab/orders_service.rb
287
291
  - app/services/lab/results_service.rb
@@ -295,6 +299,8 @@ files:
295
299
  - db/migrate/20210407071728_create_lab_lims_failed_imports.rb
296
300
  - db/migrate/20210610095024_fix_numeric_results_value_type.rb
297
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
298
304
  - lib/auto12epl.rb
299
305
  - lib/couch_bum/couch_bum.rb
300
306
  - lib/generators/lab/install/USAGE
@@ -306,6 +312,7 @@ files:
306
312
  - lib/lab/engine.rb
307
313
  - lib/lab/version.rb
308
314
  - lib/logger_multiplexor.rb
315
+ - lib/mahis_emr_api_lab.rb
309
316
  - lib/tasks/lab_tasks.rake
310
317
  - lib/tasks/loaders/data/reasons-for-test.csv
311
318
  - lib/tasks/loaders/data/test-measures.csv