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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa0a902d0f6f1159525e92598deb3ee77684c17c12a52078fa20d44cf2cafec1
4
- data.tar.gz: a0b8c8d773c498dc77b4beaeb460b491695785cca91245bf62149d434e1e4841
3
+ metadata.gz: bbe2fcb401fa3f57e0cbc85561f9ef2152e0b1736ca33ea08e36fd8aaffbeeb5
4
+ data.tar.gz: e3ab952d0c5ba09a760b554c079873884b9b937bd4822e822c0b1317ca52694c
5
5
  SHA512:
6
- metadata.gz: f24e327db48725b4ddffb24e12dcd09e02c1a0965ecdeb6aae08ab547305de6053a2d93d07154ed7750639b376b51bbedaeee54ec6cd44eeeb992f6d46dd5e8e
7
- data.tar.gz: beca813e59561a368b4ad8252f82364b19c801adcead63d8c4b3f1562ebe1066b94f4d09a3abb6c1f734728836de5b719bb3deb685598127220741fcb4f42541
6
+ metadata.gz: 735e54a4be6f1e29cd34c5b1bad5bca0aab35de90630ab0eca52d339bfff626801db089d1bb80d62f5751cd680d3b14bbb7d975c6c5143db5a2ea2ba8d7324e4
7
+ data.tar.gz: 395df3bcfe287ffbe505df72c6d4256715210147a5b933d464c1ec9c617999ead6b0d9cc90eb5e903dc82ccf274ac9afc482cffd2c767756d6c2f0be499ef315
@@ -2,7 +2,8 @@
2
2
 
3
3
  module Lab
4
4
  class LabelsController < ApplicationController
5
- skip_before_action :authenticate
5
+ _callbacks = _process_action_callbacks.map(&:filter)
6
+ skip_before_action :authenticate if _callbacks.include?(:authenticate)
6
7
 
7
8
  def print_order_label
8
9
  order_id = params.require(:order_id)
@@ -2,7 +2,6 @@
2
2
 
3
3
  module Lab
4
4
  class OrdersController < ApplicationController
5
- skip_before_action :authenticate, only: %i[order_status order_result summary]
6
5
  before_action :authenticate_request, only: %i[order_status order_result summary]
7
6
 
8
7
  def create
@@ -25,10 +24,19 @@ module Lab
25
24
  end
26
25
 
27
26
  def index
28
- filters = params.slice(:patient_id, :accession_number, :date, :status)
27
+ filters = params.permit(%i[patient_id patient accession_number date status])
29
28
 
30
- Lab::UpdatePatientOrdersJob.perform_later(filters[:patient_id]) if filters[:patient_id]
31
- render json: OrdersSearchService.find_orders(filters)
29
+ id = filters[:patient_id] || filters[:patient]
30
+
31
+ patient = Patient.find(id) if filters[:patient_id] || filters[:patient]
32
+
33
+ Lab::UpdatePatientOrdersJob.perform_later(patient.id) if filters[:patient_id] || filters[:patient]
34
+ orders = OrdersSearchService.find_orders(filters)
35
+ begin
36
+ render json: orders.reload, status: :ok
37
+ rescue StandardError
38
+ render json: orders
39
+ end
32
40
  end
33
41
 
34
42
  def verify_tracking_number
@@ -68,27 +76,8 @@ module Lab
68
76
  private
69
77
 
70
78
  def authenticate_request
71
- header = request.headers['Authorization']
72
- content = header.split(' ')
73
- auth_scheme = content.first
74
- unless header
75
- errors = ['Authorization token required']
76
- render json: { errors: errors }, status: :unauthorized
77
- return false
78
- end
79
- unless auth_scheme == 'Bearer'
80
- errors = ['Authorization token bearer scheme required']
81
- render json: { errors: errors }, status: :unauthorized
82
- return false
83
- end
84
-
85
- process_token(content.last)
86
- end
87
-
88
- def process_token(token)
89
- browser = Browser.new(request.user_agent)
90
- decoded = Lab::JsonWebTokenService.decode(token, request.remote_ip + browser.name + browser.version)
91
- user(decoded)
79
+ decoded_user = authorize_request
80
+ user(decoded_user)
92
81
  end
93
82
 
94
83
  def user(decoded)
@@ -5,11 +5,12 @@ module Lab
5
5
  def create
6
6
  result_params = params.require(:result)
7
7
  .permit([:encounter_id,
8
+ :encounter,
8
9
  :date,
9
10
  { measures: [:value,
10
11
  :value_type,
11
12
  :value_modifier,
12
- { indicator: [:concept_id] }] }])
13
+ { indicator: %i[concept_id concept] }] }])
13
14
 
14
15
  result = Lab::ResultsService.create_results(params[:test_id], result_params, 'user entered')
15
16
 
@@ -7,7 +7,6 @@ module Lab
7
7
 
8
8
  specimen_types = ConceptsService.specimen_types(name: filters['name'],
9
9
  test_type: filters['test_type'])
10
- .as_json(only: %w[concept_id name])
11
10
 
12
11
  render json: specimen_types
13
12
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lab
4
+ class TestMethodsController < ApplicationController
5
+ def index
6
+ render json: ConceptsService.test_methods(params.require(:nlims_code))
7
+ end
8
+ end
9
+ end
@@ -7,7 +7,7 @@ module Lab
7
7
 
8
8
  test_types = ConceptsService.test_types(name: filters['name'],
9
9
  specimen_type: filters['specimen_type'])
10
- .as_json(only: %w[concept_id name])
10
+
11
11
 
12
12
  render json: test_types
13
13
  end
@@ -3,9 +3,7 @@
3
3
  module Lab
4
4
  class TestsController < ApplicationController
5
5
  def index
6
- filters = params.slice(:order_date, :accession_number, :patient_id, :test_type_id, :specimen_type_id,
7
- :pending_results)
8
-
6
+ filters = params.permit(%i[order_date accession_number patient patient_id test_type_id specimen_type_id pending_results])
9
7
  tests = service.find_tests(filters)
10
8
  render json: tests
11
9
  end
@@ -3,7 +3,8 @@
3
3
  # This controller handles creation and authentication of LIMS User
4
4
  module Lab
5
5
  class UsersController < ::ApplicationController
6
- skip_before_action :authenticate
6
+ _callbacks = _process_action_callbacks.map(&:filter)
7
+ skip_before_action :authenticate if _callbacks.include?(:authenticate)
7
8
  # create a LIMS User that will be responsible for sending lab results
8
9
  def create
9
10
  user_params = params.permit(:username, :password)
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lab
4
+ ##
5
+ # Push an order to LIMS.
6
+ class ProcessLabResultJob < ApplicationJob
7
+ queue_as :default
8
+ def perform(results_obs_id, serializer, result_enter_by)
9
+ results_obs = Lab::LabResult.find(results_obs_id)
10
+ Lab::ResultsService.process_result_completion(results_obs, serializer, result_enter_by)
11
+ end
12
+ end
13
+ end
@@ -41,7 +41,10 @@ module Lab
41
41
 
42
42
  default_scope do
43
43
  joins(:order_type)
44
- .merge(OrderType.where(name: Lab::Metadata::ORDER_TYPE_NAME))
44
+ .merge(OrderType.where(name: [
45
+ Lab::Metadata::ORDER_TYPE_NAME,
46
+ Lab::Metadata::HTS_ORDER_TYPE_NAME
47
+ ]))
45
48
  .where.not(concept_id: ConceptName.where(name: 'Tests ordered').select(:concept_id))
46
49
  end
47
50
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  module Lab
4
4
  class LabResult < Observation
5
+ def children
6
+ Observation.where(obs_group_id: obs_id, voided: 0)
7
+ end
8
+
5
9
  alias measures children
6
10
 
7
11
  default_scope do
@@ -8,7 +8,7 @@ module Lab
8
8
 
9
9
  has_one :result,
10
10
  -> { where(concept: ConceptName.where(name: Lab::Metadata::TEST_RESULT_CONCEPT_NAME)) },
11
- class_name: '::Lab::LabResult',
11
+ class_name: 'Lab::LabResult',
12
12
  foreign_key: :obs_group_id
13
13
 
14
14
  def void(reason)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # model managing extra details for orders
4
+ class Lab::OrderExtension < ApplicationRecord
5
+ include Voidable
6
+ self.table_name = :order_extension
7
+ self.primary_key = :order_extension_id
8
+
9
+ default_scope { where(voided: 0) }
10
+ scope :voided, -> { unscoped.where.not(voided: 0) }
11
+
12
+ belongs_to :order, foreign_key: :order_id
13
+ belongs_to :creator, class_name: 'User', foreign_key: :creator
14
+ end
@@ -2,18 +2,27 @@
2
2
 
3
3
  module Lab
4
4
  module LabOrderSerializer
5
- def self.serialize_order(order, tests: nil, requesting_clinician: nil, reason_for_test: nil, target_lab: nil)
5
+ def self.serialize_order(order, tests: nil, requesting_clinician: nil, reason_for_test: nil, target_lab: nil, comment_to_fulfiller: nil)
6
6
  tests ||= order.voided == 1 ? voided_tests(order) : order.tests
7
7
  requesting_clinician ||= order.requesting_clinician
8
+ comment_to_fulfiller ||= order.comment_to_fulfiller
8
9
  reason_for_test ||= order.reason_for_test
9
10
  target_lab = target_lab&.value_text || order.target_lab&.value_text || Location.current_health_center&.name
10
11
 
12
+ encounter = Encounter.find_by_encounter_id(order.encounter_id)
13
+ program = Program.find_by_program_id(encounter.program_id)
14
+
11
15
  ActiveSupport::HashWithIndifferentAccess.new(
12
16
  {
13
17
  id: order.order_id,
18
+ order_type_id: order.order_type_id,
14
19
  order_id: order.order_id, # Deprecated: Link to :id
15
20
  encounter_id: order.encounter_id,
16
- order_date: order.start_date,
21
+ order_date: order.date_created,
22
+ location_id: encounter.location_id,
23
+ program_id: encounter.program_id,
24
+ program_name: program.name,
25
+ # order_date: order.start_date,
17
26
  patient_id: order.patient_id,
18
27
  accession_number: order.accession_number,
19
28
  specimen: {
@@ -21,7 +30,8 @@ module Lab
21
30
  name: concept_name(order.concept_id)
22
31
  },
23
32
  requesting_clinician: requesting_clinician&.value_text,
24
- target_lab:,
33
+ target_lab: target_lab,
34
+ comment_to_fulfiller: comment_to_fulfiller.respond_to?(:value_text) ? comment_to_fulfiller.value_text : comment_to_fulfiller,
25
35
  reason_for_test: {
26
36
  concept_id: reason_for_test&.value_coded,
27
37
  name: concept_name(reason_for_test&.value_coded)
@@ -33,7 +43,9 @@ module Lab
33
43
  {
34
44
  id: test.obs_id,
35
45
  concept_id: test.value_coded,
46
+ uuid: test.uuid,
36
47
  name: concept_name(test.value_coded),
48
+ test_method: test_method(order, test.value_coded),
37
49
  result: result_obs && ResultSerializer.serialize(result_obs)
38
50
  }
39
51
  end
@@ -41,10 +53,21 @@ module Lab
41
53
  )
42
54
  end
43
55
 
56
+ def self.test_method(order, _concept_id)
57
+ obs = ::Observation
58
+ .select(:value_coded)
59
+ .where(concept_id: ConceptName.find_by_name(Metadata::TEST_METHOD_CONCEPT_NAME).concept_id, order_id: order.id)
60
+ .first
61
+ {
62
+ concept_id: obs&.value_coded,
63
+ name: ConceptName.find_by_concept_id(obs&.value_coded)&.name
64
+ }
65
+ end
66
+
44
67
  def self.concept_name(concept_id)
45
68
  return concept_id unless concept_id
46
69
 
47
- ConceptName.select(:name).find_by_concept_id(concept_id)&.name
70
+ ::ConceptAttribute.find_by(concept_id:, attribute_type: ConceptAttributeType.test_catalogue_name)&.value_reference
48
71
  end
49
72
 
50
73
  def self.voided_tests(order)
@@ -8,6 +8,13 @@ module Lab
8
8
  result.children.map do |measure|
9
9
  value, value_type = read_value(measure)
10
10
  concept_name = ConceptName.find_by_concept_id(measure.concept_id)
11
+
12
+ program_id = ""
13
+ if measure.obs_id.present?
14
+ obs = Observation.find(measure.obs_id)
15
+ encounter = Encounter.find(obs.encounter_id)
16
+ program_id = encounter.program_id
17
+ end
11
18
 
12
19
  {
13
20
  id: measure.obs_id,
@@ -18,14 +25,15 @@ module Lab
18
25
  date: measure.obs_datetime,
19
26
  value:,
20
27
  value_type:,
21
- value_modifier: measure.value_modifier
28
+ value_modifier: measure.value_modifier,
29
+ program_id: program_id
22
30
  }
23
31
  end
24
32
  end
25
33
 
26
34
  def self.read_value(measure)
27
35
  %w[value_numeric value_coded value_boolean value_text].each do |field|
28
- value = measure.send(field)
36
+ value = measure.send(field) if measure.respond_to?(field)
29
37
 
30
38
  return [value, field.split('_')[1]] if value
31
39
  end
@@ -1,3 +1,4 @@
1
+ # rubocop:disable Lint/UnreachableLoop
1
2
  # frozen_string_literal: true
2
3
 
3
4
  module Lab
@@ -5,25 +6,47 @@ module Lab
5
6
  def self.serialize(test, order: nil, result: nil)
6
7
  order ||= test.order
7
8
  result ||= test.result
8
-
9
9
  {
10
10
  id: test.obs_id,
11
+ test_uuid: test.uuid,
11
12
  concept_id: test.value_coded,
13
+ concept_uuid: test.value_coded ? Concept.find(test.value_coded)&.uuid : nil,
12
14
  name: ConceptName.find_by_concept_id(test.value_coded)&.name,
13
15
  order: {
14
16
  id: order.order_id,
15
17
  concept_id: order.concept_id,
18
+ concept_uuid: order.concept_id ? Concept.find(order.concept_id)&.uuid : nil,
16
19
  name: ConceptName.find_by_concept_id(order.concept_id)&.name,
17
20
  accession_number: order.accession_number
18
21
  },
22
+ measures: result_mesures(result),
19
23
  result: if result
20
24
  {
21
25
  id: result.obs_id,
26
+ uuid: result.uuid,
22
27
  modifier: result.value_modifier,
23
28
  value: result.value_text
24
29
  }
25
30
  end
26
31
  }
27
32
  end
33
+
34
+ def self.result_mesures(result)
35
+ if result&.measures.present?
36
+ return result&.measures&.map do |measure|
37
+ m = {}
38
+ m[:uuid] = measure.uuid
39
+ m[:concept_id] = measure.concept_id
40
+ m[:name] = ConceptName.find_by_concept_id(measure.concept_id)&.name
41
+ m[:modifier] = measure.value_modifier
42
+ m[:value] = measure&.value_text || measure&.value_numeric || measure&.value_boolean || measure&.value_coded || measure&.value_datetime || measure&.value_drug || measure&.value_complex || measure&.value_group
43
+ m
44
+ end
45
+ end
46
+
47
+ nil
48
+ end
28
49
  end
29
50
  end
51
+
52
+ # rubocop:enable Lint/UnreachableLoop
@@ -41,8 +41,8 @@ module Lab
41
41
  year = format_year(date.year)
42
42
  month = format_month(date.month)
43
43
  day = format_day(date.day)
44
-
45
- "X#{site_code}#{year}#{month}#{day}#{counter}"
44
+ time_acc_generated = Time.now.strftime('%H%M')
45
+ "X#{site_code}#{year}#{month}#{day}#{counter}#{time_acc_generated}"
46
46
  end
47
47
 
48
48
  def format_year(year)
@@ -6,6 +6,9 @@ module Lab
6
6
  class << self
7
7
  def create_acknowledgement(params)
8
8
  order = Order.find(params[:order_id])
9
+
10
+ Lab::LabAcknowledgement.find_by(order_id: order.id, test: params[:test])&.destroy
11
+
9
12
  Lab::LabAcknowledgement.create!(order_id: order.id, test: params[:test], pushed: false,
10
13
  acknowledgement_type: params[:entered_by] == 'LIMS' ? 'test_results_delivered_to_site_electronically' : 'test_results_delivered_to_site_manually',
11
14
  date_received: params[:date_received])
@@ -27,7 +30,7 @@ module Lab
27
30
  Rails.logger.info("Updating acknowledgement ##{acknowledgement_dto[:tracking_number]} in LIMS")
28
31
  response = lims_api.acknowledge(acknowledgement_dto)
29
32
  Rails.logger.info("Info #{response}")
30
- if response['status'] == 200 || response['message'] == 'results already delivered for test name given'
33
+ if ['results already delivered for test name given', 'test result acknowledged successfully', 'test result already acknowledged electronically at facility'].include?(response['message'])
31
34
  acknowledgement.pushed = true
32
35
  acknowledgement.date_pushed = Time.now
33
36
  acknowledgement.save!
@@ -3,14 +3,45 @@
3
3
  module Lab
4
4
  # A read-only repository of sort for all lab-centric concepts.
5
5
  module ConceptsService
6
+ def self.test_methods(nlims_code)
7
+ return ActiveRecord::Base.connection.select_all <<~SQL
8
+ SELECT cn.name, cn.concept_id
9
+ FROM concept_set cs
10
+ INNER JOIN concept_name cn ON cn.concept_id = cs.concept_id
11
+ INNER JOIN concept_attribute ca ON ca.value_reference = #{ActiveRecord::Base.connection.quote(nlims_code)}
12
+ AND ca.attribute_type_id = 20
13
+ WHERE cs.concept_id IN (SELECT concept_set.concept_id
14
+ FROM concept_set
15
+ WHERE concept_set.concept_set IN (SELECT concept_name.concept_id
16
+ FROM concept_name
17
+ WHERE concept_name.voided = 0
18
+ AND concept_name.name = 'Recommended test method'))
19
+ AND cs.concept_set IN (SELECT concept_set.concept_id
20
+ FROM concept_set
21
+ WHERE concept_set.concept_set IN (SELECT concept_name.concept_id
22
+ FROM concept_name
23
+ WHERE concept_name.voided = 0
24
+ AND concept_name.name = 'Test type')
25
+ AND concept_set.concept_id = ca.concept_id
26
+ )
27
+ GROUP BY cn.concept_id
28
+ SQL
29
+ end
30
+
6
31
  def self.test_types(name: nil, specimen_type: nil)
7
32
  test_types = ConceptSet.find_members_by_name(Lab::Metadata::TEST_TYPE_CONCEPT_NAME)
8
33
  test_types = test_types.filter_members(name:) if name
9
34
 
10
35
  unless specimen_type
11
- return test_types.joins('INNER JOIN concept_name ON concept_set.concept_id = concept_name.concept_id AND concept_name.voided = 0 AND concept_name.locale_preferred = 1')
12
- .select('concept_name.name, concept_name.concept_id')
13
- .group('concept_name.concept_id')
36
+ return ActiveRecord::Base.connection.select_all <<~SQL
37
+ SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code
38
+ FROM concept_attribute ca
39
+ INNER JOIN concept_attribute ca2 ON ca.concept_id = ca2.concept_id
40
+ AND ca2.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
41
+ WHERE ca.attribute_type_id = #{ConceptAttributeType.test_catalogue_name.concept_attribute_type_id}
42
+ AND ca.concept_id IN (#{test_types.select(:concept_id).to_sql})
43
+ GROUP BY ca.concept_id
44
+ SQL
14
45
  end
15
46
 
16
47
  # Filter out only those test types that have the specified specimen
@@ -24,25 +55,37 @@ module Lab
24
55
  concept_set: test_types.select(:concept_id)
25
56
  )
26
57
 
27
- concept_set.joins('INNER JOIN concept_name ON concept_set.concept_set = concept_name.concept_id')
28
- .select('concept_name.concept_id, concept_name.name')
29
- .group('concept_name.concept_id')
58
+ return ActiveRecord::Base.connection.select_all <<~SQL
59
+ SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code
60
+ FROM concept_attribute ca
61
+ INNER JOIN concept_attribute ca2 ON ca.concept_id = ca2.concept_id
62
+ AND ca2.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
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})
65
+ GROUP BY ca.concept_id
66
+ SQL
30
67
  end
31
68
 
32
69
  def self.specimen_types(name: nil, test_type: nil)
33
70
  specimen_types = ConceptSet.find_members_by_name(Lab::Metadata::SPECIMEN_TYPE_CONCEPT_NAME)
34
- specimen_types = specimen_types.filter_members(name:) if name
71
+ specimen_types = specimen_types.filter_members(name: name) if name
35
72
 
36
73
  unless test_type
37
- return specimen_types.select('concept_name.concept_id, concept_name.name')
38
- .joins('INNER JOIN concept_name ON concept_name.concept_id = concept_set.concept_id')
39
- .group('concept_name.concept_id')
74
+ return ActiveRecord::Base.connection.select_all <<~SQL
75
+ SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code
76
+ FROM concept_attribute ca
77
+ INNER JOIN concept_attribute ca2 ON ca.concept_id = ca2.concept_id
78
+ AND ca2.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
79
+ WHERE ca.attribute_type_id = #{ConceptAttributeType.test_catalogue_name.concept_attribute_type_id}
80
+ AND ca.concept_id IN (#{specimen_types.select(:concept_id).to_sql})
81
+ GROUP BY ca.concept_id
82
+ SQL
40
83
  end
41
84
 
42
85
  # Retrieve only those specimen types that belong to concept
43
86
  # set of the selected test_type
44
87
  test_types = ConceptSet.find_members_by_name(Lab::Metadata::TEST_TYPE_CONCEPT_NAME)
45
- .filter_members(name: test_type)
88
+ .filter_members(name: test_type&.strip)
46
89
  .select(:concept_id)
47
90
 
48
91
  concept_set = ConceptSet.where(
@@ -50,33 +93,45 @@ module Lab
50
93
  concept_set: test_types
51
94
  )
52
95
 
53
- concept_set.select('concept_name.concept_id, concept_name.name')
54
- .joins('INNER JOIN concept_name ON concept_name.concept_id = concept_set.concept_id')
55
- .group('concept_name.concept_id')
96
+ return ActiveRecord::Base.connection.select_all <<~SQL
97
+ SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code
98
+ FROM concept_attribute ca
99
+ INNER JOIN concept_attribute ca2 ON ca.concept_id = ca2.concept_id
100
+ AND ca2.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
101
+ WHERE ca.attribute_type_id = #{ConceptAttributeType.test_catalogue_name.concept_attribute_type_id}
102
+ AND ca.concept_id IN (#{concept_set.pluck(:concept_id).push(0).join(',')})
103
+ GROUP BY ca.concept_id
104
+ SQL
56
105
  end
57
106
 
58
107
  def self.test_result_indicators(test_type_id)
59
108
  # Verify that the specified test_type is indeed a test_type
60
109
  test = ConceptSet.find_members_by_name(Lab::Metadata::TEST_TYPE_CONCEPT_NAME)
61
- .where(concept_id: test_type_id)
110
+ .where(concept_id: Concept.find(test_type_id)&.id)
62
111
  .select(:concept_id)
63
112
 
64
113
  # From the members above, filter out only those concepts that are result indicators
65
114
  measures = ConceptSet.find_members_by_name(Lab::Metadata::TEST_RESULT_INDICATOR_CONCEPT_NAME)
66
115
  .select(:concept_id)
67
116
 
68
- ConceptSet.where(concept_set: measures, concept_id: test)
69
- .joins("INNER JOIN concept_name AS measure ON measure.concept_id = concept_set.concept_set AND (measure.locale_preferred = 1 OR measure.concept_name_type = 'SHORT' OR measure.concept_name_type = 'FULLY_SPECIFIED')")
70
- .select('measure.concept_id, measure.name')
71
- .group('measure.concept_id')
72
- .map { |concept| { name: concept.name, concept_id: concept.concept_id } }
117
+ sets = ConceptSet.where(concept_set: measures, concept_id: test)
118
+
119
+ return ActiveRecord::Base.connection.select_all <<~SQL
120
+ SELECT ca.concept_id, ca.value_reference as name, ca2.value_reference as nlims_code
121
+ FROM concept_attribute ca
122
+ INNER JOIN concept_attribute ca2 ON ca.concept_id = ca2.concept_id
123
+ AND ca2.attribute_type_id = #{ConceptAttributeType.nlims_code.concept_attribute_type_id}
124
+ WHERE ca.attribute_type_id = #{ConceptAttributeType.test_catalogue_name.concept_attribute_type_id}
125
+ AND ca.concept_id IN (#{sets.pluck(:concept_set).push(0).join(',')})
126
+ GROUP BY ca.concept_id
127
+ SQL
73
128
  end
74
129
 
75
130
  def self.reasons_for_test
76
131
  ConceptSet.find_members_by_name(Lab::Metadata::REASON_FOR_TEST_CONCEPT_NAME)
77
132
  .joins('INNER JOIN concept_name ON concept_name.concept_id = concept_set.concept_id')
78
- .select('concept_name.concept_id, concept_name.name')
79
- .map { |concept| { name: concept.name, concept_id: concept.concept_id } }
133
+ .select('concept_name.concept_id, concept_name.name, concept_name.uuid')
134
+ .map { |concept| { name: concept.name, concept_id: concept.concept_id, uuid: concept.uuid } }
80
135
  end
81
136
  end
82
137
  end
@@ -22,7 +22,7 @@ module Lab
22
22
  logger.debug("Found #{acknowledgements.size} acknowledgements...")
23
23
  acknowledgements.each do |acknowledgement|
24
24
  Lab::AcknowledgementService.push_acknowledgement(acknowledgement, @lims_api)
25
- rescue GatewayError => e
25
+ rescue StandardError => e
26
26
  logger.error("Failed to push acknowledgement ##{acknowledgement.order_id}: #{e.class} - #{e.message}")
27
27
  end
28
28