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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c71682551eb38f2c025993390d6d32f064be0fd35da372dede313882f12c6ad8
4
- data.tar.gz: f59352b4aceb5d7cf891cb094ef9b213f7e191d453936ca97396101d789e3846
3
+ metadata.gz: 306b46064eb5c58668eb1f4598d99258f1e76b69db42b90abe33d35ddd2434b0
4
+ data.tar.gz: f367e1fdf5101dd25a2e64f83f61ac6eeffe4fee2007846da5037c18083d1b96
5
5
  SHA512:
6
- metadata.gz: 2db7d3ee59f9cd0e94c2eccc5f7a4514d9c25479e6e31dcce709aa3005934b6c3a2b794fa8e297e86a792f9c311a12628bd1c117d8202aafe49da6c72606643a
7
- data.tar.gz: 025253b76aa0cb65c237e03aecbcf7010a9a847b7c249d46af7c51330fdbf6ef03b6ee82051aa12271984511091a6d639e46b61c9ca51a3714f1b30b806ba623
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.name,
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
- concept_name = ConceptName.find_by_concept_id(measure.concept_id)
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: concept_name&.concept_id,
23
- name: concept_name&.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 = 20
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(:concept_id).to_sql})
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/couch_db_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(ConceptName.find_by_concept_id!(test.value_coded).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.zero?
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 'Viral Load' if test_name.casecmp?('HIV Viral load')
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)
@@ -216,7 +216,7 @@ module Lab
216
216
  end
217
217
 
218
218
  next if test.result || test_results['results'].blank?
219
-
219
+
220
220
  result_date = Time.now
221
221
  measures = test_results['results'].map do |indicator, value|
222
222
  measure = find_measure(order, indicator, value)
@@ -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.zero?
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
- ConceptName.joins("INNER JOIN concept_attribute ON concept_attribute.concept_id = concept_name.concept_id")
82
- .where("concept_attribute.value_reference = ?", CGI.unescapeHTML(name))
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.id if Encounter.column_names.include?('provider_id')
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 = Lab::LabTest.find(test_id) rescue nil
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
- # delay job by a second
42
- ProcessLabResultJob.set(wait: 1.second).perform_later(results_obs.id, serializer, result_enter_by)
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
- 'Orde date': Order.columns.include?('start_date') ? order.start_date : order.date_created,
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
- encounter_type = EncounterType.find_by_name!(Lab::Metadata::ENCOUNTER_TYPE_NAME)
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
- encounter.type = encounter_type
92
- encounter.encounter_type = encounter_type if (encounter&.encounter_type.nil? || encounter&.type.nil?)
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, :concept))&.id
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lab
4
- VERSION = '2.1.3'
4
+ VERSION = '2.1.4-alpha'
5
5
  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.3
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-22 00:00:00.000000000 Z
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/couch_db_api.rb
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: '0'
345
+ version: 1.3.1
345
346
  requirements: []
346
347
  rubygems_version: 3.4.1
347
348
  signing_key: