his_emr_api_lab 2.1.9.pre.alpha → 2.2.1
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/orders_controller.rb +21 -2
- data/app/serializers/lab/lab_order_serializer.rb +3 -3
- data/app/services/lab/acknowledgement_service.rb +8 -4
- data/app/services/lab/lims/acknowledgement_worker.rb +5 -3
- data/app/services/lab/lims/api/rest_api.rb +15 -11
- data/app/services/lab/lims/pull_worker.rb +4 -3
- data/app/services/lab/lims/push_worker.rb +15 -8
- data/app/services/lab/lims/worker.rb +13 -13
- data/app/services/lab/orders_service.rb +2 -1
- data/lib/lab/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c59744a50921b0f2244c94e052d4d66cbf39096485f99755e84f9cb66c311a0b
|
|
4
|
+
data.tar.gz: 989cc056aa59acbcd9d24977d3fc34b330cd1793db5d35908ccce44ca08dab34
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 22429cb56bfb9c5af91e5b44e2da11b66551ac89d084eeea45f5b16bb06daf5cc4f99d41cb5f2e4f3d5a38443fb62c15e5a30a14c5e5fff85006a3233d8f71de
|
|
7
|
+
data.tar.gz: 622a040596dc790f1d5a3c05469f7afe131badeca6313eb2412f8da378e632b7b28643c6a12eba6f02c618b9bed680722a635ce6e2e57ea6863872eb5e9b0966
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Lab
|
|
4
4
|
class OrdersController < ApplicationController
|
|
5
|
+
skip_before_action :authenticate, only: %i[order_status order_result summary]
|
|
5
6
|
before_action :authenticate_request, only: %i[order_status order_result summary]
|
|
6
7
|
|
|
7
8
|
def create
|
|
@@ -77,8 +78,26 @@ module Lab
|
|
|
77
78
|
private
|
|
78
79
|
|
|
79
80
|
def authenticate_request
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
header = request.headers['Authorization']
|
|
82
|
+
content = header.split(' ')
|
|
83
|
+
auth_scheme = content.first
|
|
84
|
+
unless header
|
|
85
|
+
errors = ['Authorization token required']
|
|
86
|
+
render json: { errors: errors }, status: :unauthorized
|
|
87
|
+
return false
|
|
88
|
+
end
|
|
89
|
+
unless auth_scheme == 'Bearer'
|
|
90
|
+
errors = ['Authorization token bearer scheme required']
|
|
91
|
+
render json: { errors: errors }, status: :unauthorized
|
|
92
|
+
return false
|
|
93
|
+
end
|
|
94
|
+
process_token(content.last)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def process_token(token)
|
|
98
|
+
browser = Browser.new(request.user_agent)
|
|
99
|
+
decoded = Lab::JsonWebTokenService.decode(token, request.remote_ip + browser.name + browser.version)
|
|
100
|
+
user(decoded)
|
|
82
101
|
end
|
|
83
102
|
|
|
84
103
|
def user(decoded)
|
|
@@ -10,7 +10,7 @@ module Lab
|
|
|
10
10
|
target_lab = target_lab&.value_text || order.target_lab&.value_text || Location.current_health_center&.name
|
|
11
11
|
|
|
12
12
|
encounter = Encounter.find_by_encounter_id(order.encounter_id)
|
|
13
|
-
program = Program.find_by_program_id(encounter
|
|
13
|
+
program = Program.find_by_program_id(encounter&.program_id)
|
|
14
14
|
|
|
15
15
|
ActiveSupport::HashWithIndifferentAccess.new(
|
|
16
16
|
{
|
|
@@ -19,8 +19,8 @@ module Lab
|
|
|
19
19
|
order_id: order.order_id, # Deprecated: Link to :id
|
|
20
20
|
encounter_id: order.encounter_id,
|
|
21
21
|
order_date: order.start_date,
|
|
22
|
-
location_id: encounter
|
|
23
|
-
program_id: encounter
|
|
22
|
+
location_id: encounter&.location_id,
|
|
23
|
+
program_id: encounter&.program_id,
|
|
24
24
|
program_name: program&.name,
|
|
25
25
|
patient_id: order.patient_id,
|
|
26
26
|
accession_number: order.accession_number,
|
|
@@ -14,9 +14,12 @@ module Lab
|
|
|
14
14
|
date_received: params[:date_received])
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def acknowledgements_pending_sync(batch_size)
|
|
18
|
-
Lab::LabAcknowledgement.where(pushed: false)
|
|
19
|
-
|
|
17
|
+
def acknowledgements_pending_sync(batch_size, start_date: nil)
|
|
18
|
+
query = Lab::LabAcknowledgement.joins(:order).where(pushed: false)
|
|
19
|
+
|
|
20
|
+
query = query.where('orders.date_created >= ?', start_date) if start_date
|
|
21
|
+
|
|
22
|
+
query.limit(batch_size)
|
|
20
23
|
end
|
|
21
24
|
|
|
22
25
|
def push_acknowledgement(acknowledgement, lims_api)
|
|
@@ -30,7 +33,8 @@ module Lab
|
|
|
30
33
|
Rails.logger.info("Updating acknowledgement ##{acknowledgement_dto[:tracking_number]} in LIMS")
|
|
31
34
|
response = lims_api.acknowledge(acknowledgement_dto)
|
|
32
35
|
Rails.logger.info("Info #{response}")
|
|
33
|
-
if ['results already delivered for test name given', 'test result acknowledged successfully',
|
|
36
|
+
if ['results already delivered for test name given', 'test result acknowledged successfully',
|
|
37
|
+
'test result already acknowledged electronically at facility'].include?(response['message'])
|
|
34
38
|
acknowledgement.pushed = true
|
|
35
39
|
acknowledgement.date_pushed = Time.now
|
|
36
40
|
acknowledgement.save!
|
|
@@ -4,20 +4,22 @@ module Lab
|
|
|
4
4
|
module Lims
|
|
5
5
|
# This class is responsible for handling the acknowledgement of lab orders
|
|
6
6
|
class AcknowledgementWorker
|
|
7
|
-
attr_reader :lims_api
|
|
7
|
+
attr_reader :lims_api, :start_date
|
|
8
8
|
|
|
9
9
|
include Utils # for logger
|
|
10
10
|
|
|
11
11
|
SECONDS_TO_WAIT_FOR_ORDERS = 30
|
|
12
12
|
|
|
13
|
-
def initialize(lims_api)
|
|
13
|
+
def initialize(lims_api, start_date: nil)
|
|
14
14
|
@lims_api = lims_api
|
|
15
|
+
@start_date = start_date
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def push_acknowledgement(batch_size: 1000, wait: false)
|
|
18
19
|
loop do
|
|
19
20
|
logger.info('Looking for new acknowledgements to push to LIMS...')
|
|
20
|
-
acknowledgements = Lab::AcknowledgementService.acknowledgements_pending_sync(batch_size
|
|
21
|
+
acknowledgements = Lab::AcknowledgementService.acknowledgements_pending_sync(batch_size,
|
|
22
|
+
start_date: start_date).all
|
|
21
23
|
|
|
22
24
|
logger.debug("Found #{acknowledgements.size} acknowledgements...")
|
|
23
25
|
acknowledgements.each do |acknowledgement|
|
|
@@ -74,8 +74,8 @@ module Lab
|
|
|
74
74
|
{ tracking_number: order_dto[:tracking_number] }
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
def consume_orders(*_args, patient_id: nil, **_kwargs)
|
|
78
|
-
orders_pending_updates(patient_id).each do |order|
|
|
77
|
+
def consume_orders(*_args, patient_id: nil, start_date: nil, **_kwargs)
|
|
78
|
+
orders_pending_updates(patient_id, start_date: start_date).each do |order|
|
|
79
79
|
order_dto = Lab::Lims::OrderSerializer.serialize_order(order)
|
|
80
80
|
|
|
81
81
|
# Always fetch the full order from NLIMS to get status trails
|
|
@@ -577,43 +577,47 @@ module Lab
|
|
|
577
577
|
}
|
|
578
578
|
end
|
|
579
579
|
|
|
580
|
-
def orders_pending_updates(patient_id = nil)
|
|
580
|
+
def orders_pending_updates(patient_id = nil, start_date: nil)
|
|
581
581
|
Rails.logger.info('Looking for orders that need to be updated...')
|
|
582
582
|
orders = {}
|
|
583
583
|
|
|
584
|
-
orders_without_specimen(patient_id).each { |order| orders[order.order_id] = order }
|
|
585
|
-
orders_without_results(patient_id).each { |order| orders[order.order_id] = order }
|
|
586
|
-
orders_without_reason(patient_id).each { |order| orders[order.order_id] = order }
|
|
584
|
+
orders_without_specimen(patient_id, start_date: start_date).each { |order| orders[order.order_id] = order }
|
|
585
|
+
orders_without_results(patient_id, start_date: start_date).each { |order| orders[order.order_id] = order }
|
|
586
|
+
orders_without_reason(patient_id, start_date: start_date).each { |order| orders[order.order_id] = order }
|
|
587
587
|
|
|
588
588
|
orders.values
|
|
589
589
|
end
|
|
590
590
|
|
|
591
|
-
def orders_without_specimen(patient_id = nil)
|
|
591
|
+
def orders_without_specimen(patient_id = nil, start_date: nil)
|
|
592
592
|
Rails.logger.debug('Looking for orders without a specimen')
|
|
593
593
|
unknown_specimen = ConceptName.where(name: Lab::Metadata::UNKNOWN_SPECIMEN)
|
|
594
594
|
.select(:concept_id)
|
|
595
595
|
orders = Lab::LabOrder.where(concept_id: unknown_specimen)
|
|
596
596
|
.where.not(accession_number: Lab::LimsOrderMapping.select(:lims_id))
|
|
597
597
|
orders = orders.where(patient_id:) if patient_id
|
|
598
|
+
orders = orders.where('orders.date_created >= ?', start_date) if start_date
|
|
598
599
|
|
|
599
600
|
orders
|
|
600
601
|
end
|
|
601
602
|
|
|
602
|
-
def orders_without_results(patient_id = nil)
|
|
603
|
+
def orders_without_results(patient_id = nil, start_date: nil)
|
|
603
604
|
Rails.logger.debug('Looking for orders without a result')
|
|
604
605
|
# Lab::OrdersSearchService.find_orders_without_results(patient_id: patient_id)
|
|
605
606
|
# .where.not(accession_number: Lab::LimsOrderMapping.select(:lims_id).where("pulled_at IS NULL"))
|
|
606
|
-
Lab::OrdersSearchService.find_orders_without_results(patient_id:)
|
|
607
|
-
|
|
607
|
+
orders = Lab::OrdersSearchService.find_orders_without_results(patient_id:)
|
|
608
|
+
.where(order_id: Lab::LimsOrderMapping.select(:order_id))
|
|
609
|
+
orders = orders.where('orders.date_created >= ?', start_date) if start_date
|
|
610
|
+
orders
|
|
608
611
|
end
|
|
609
612
|
|
|
610
|
-
def orders_without_reason(patient_id = nil)
|
|
613
|
+
def orders_without_reason(patient_id = nil, start_date: nil)
|
|
611
614
|
Rails.logger.debug('Looking for orders without a reason for test')
|
|
612
615
|
orders = Lab::LabOrder.joins(:reason_for_test)
|
|
613
616
|
.merge(Observation.where(value_coded: nil, value_text: nil))
|
|
614
617
|
.limit(1000)
|
|
615
618
|
.where.not(accession_number: Lab::LimsOrderMapping.select(:lims_id))
|
|
616
619
|
orders = orders.where(patient_id:) if patient_id
|
|
620
|
+
orders = orders.where('orders.date_created >= ?', start_date) if start_date
|
|
617
621
|
|
|
618
622
|
orders
|
|
619
623
|
end
|
|
@@ -5,14 +5,15 @@ module Lab
|
|
|
5
5
|
##
|
|
6
6
|
# Pulls orders from a Lims API object and saves them to the local database.
|
|
7
7
|
class PullWorker
|
|
8
|
-
attr_reader :lims_api
|
|
8
|
+
attr_reader :lims_api, :start_date
|
|
9
9
|
|
|
10
10
|
include Utils # for logger
|
|
11
11
|
|
|
12
12
|
LIMS_LOG_PATH = Rails.root.join('log', 'lims')
|
|
13
13
|
|
|
14
|
-
def initialize(lims_api)
|
|
14
|
+
def initialize(lims_api, start_date: nil)
|
|
15
15
|
@lims_api = lims_api
|
|
16
|
+
@start_date = start_date
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
##
|
|
@@ -20,7 +21,7 @@ module Lab
|
|
|
20
21
|
def pull_orders(batch_size: 10_000, **)
|
|
21
22
|
logger.info("Retrieving LIMS orders starting from #{last_seq}")
|
|
22
23
|
|
|
23
|
-
lims_api.consume_orders(from: last_seq, limit: batch_size, **) do |order_dto, context|
|
|
24
|
+
lims_api.consume_orders(from: last_seq, limit: batch_size, start_date: start_date, **) do |order_dto, context|
|
|
24
25
|
logger.debug("Retrieved order ##{order_dto[:tracking_number]}: #{order_dto}")
|
|
25
26
|
|
|
26
27
|
patient = find_patient_by_nhid(order_dto[:patient][:id], order_dto[:tracking_number])
|
|
@@ -5,15 +5,16 @@ module Lab
|
|
|
5
5
|
##
|
|
6
6
|
# Pushes all local orders to a LIMS Api object.
|
|
7
7
|
class PushWorker
|
|
8
|
-
attr_reader :lims_api
|
|
8
|
+
attr_reader :lims_api, :start_date
|
|
9
9
|
|
|
10
10
|
include Utils # for logger
|
|
11
11
|
|
|
12
12
|
SECONDS_TO_WAIT_FOR_ORDERS = 30
|
|
13
13
|
START_DATE = Time.parse('2024-09-03').freeze
|
|
14
14
|
|
|
15
|
-
def initialize(lims_api)
|
|
15
|
+
def initialize(lims_api, start_date: nil)
|
|
16
16
|
@lims_api = lims_api
|
|
17
|
+
@start_date = start_date
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def push_orders(batch_size: 1000, wait: false)
|
|
@@ -81,8 +82,8 @@ module Lab
|
|
|
81
82
|
|
|
82
83
|
def void_order_in_lims(order_id)
|
|
83
84
|
order = Lab::LabOrder.joins(order_type: { name: 'Lab' })
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
.unscoped
|
|
86
|
+
.find(order_id)
|
|
86
87
|
order_dto = Lab::Lims::OrderSerializer.serialize_order(order)
|
|
87
88
|
Rails.logger.info("Deleting order ##{order_dto[:accession_number]} from LIMS")
|
|
88
89
|
lims_api.delete_order('', order_dto)
|
|
@@ -100,10 +101,16 @@ module Lab
|
|
|
100
101
|
|
|
101
102
|
def new_orders
|
|
102
103
|
Rails.logger.debug('Looking for new orders that need to be created in LIMS...')
|
|
103
|
-
Lab::LabOrder.where.not(order_id: Lab::LimsOrderMapping.all.select(:order_id))
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
query = Lab::LabOrder.where.not(order_id: Lab::LimsOrderMapping.all.select(:order_id))
|
|
105
|
+
.where("accession_number IS NOT NULL AND accession_number !=''")
|
|
106
|
+
|
|
107
|
+
query = if start_date
|
|
108
|
+
query.where('orders.date_created >= ?', start_date)
|
|
109
|
+
else
|
|
110
|
+
query.where('orders.date_created >= ? AND orders.date_created <= ?', START_DATE, Date.today + 1.day)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
query.order(date_created: :desc)
|
|
107
114
|
end
|
|
108
115
|
|
|
109
116
|
def updated_orders
|
|
@@ -9,43 +9,43 @@ module Lab
|
|
|
9
9
|
##
|
|
10
10
|
# Pull/Push orders from/to the LIMS queue (Oops meant CouchDB).
|
|
11
11
|
module Worker
|
|
12
|
-
def self.start
|
|
12
|
+
def self.start(start_date: nil)
|
|
13
13
|
User.current = Utils.lab_user
|
|
14
14
|
|
|
15
|
-
fork(
|
|
16
|
-
fork(
|
|
17
|
-
fork(
|
|
18
|
-
fork(
|
|
15
|
+
fork { start_push_worker(start_date: start_date) }
|
|
16
|
+
fork { start_pull_worker(start_date: start_date) }
|
|
17
|
+
fork { start_acknowledgement_worker(start_date: start_date) }
|
|
18
|
+
fork { start_realtime_pull_worker(start_date: start_date) } if realtime_updates_enabled?
|
|
19
19
|
|
|
20
20
|
Process.waitall
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def self.start_push_worker
|
|
23
|
+
def self.start_push_worker(start_date: nil)
|
|
24
24
|
start_worker('push_worker') do
|
|
25
|
-
worker = PushWorker.new(lims_api)
|
|
25
|
+
worker = PushWorker.new(lims_api, start_date: start_date)
|
|
26
26
|
|
|
27
27
|
worker.push_orders # (wait: true)
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
def self.start_acknowledgement_worker
|
|
31
|
+
def self.start_acknowledgement_worker(start_date: nil)
|
|
32
32
|
start_worker('acknowledgement_worker') do
|
|
33
|
-
worker = AcknowledgementWorker.new(lims_api)
|
|
33
|
+
worker = AcknowledgementWorker.new(lims_api, start_date: start_date)
|
|
34
34
|
worker.push_acknowledgement
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
def self.start_pull_worker
|
|
38
|
+
def self.start_pull_worker(start_date: nil)
|
|
39
39
|
start_worker('pull_worker') do
|
|
40
|
-
worker = PullWorker.new(lims_api)
|
|
40
|
+
worker = PullWorker.new(lims_api, start_date: start_date)
|
|
41
41
|
|
|
42
42
|
worker.pull_orders
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
def self.start_realtime_pull_worker
|
|
46
|
+
def self.start_realtime_pull_worker(start_date: nil)
|
|
47
47
|
start_worker('realtime_pull_worker') do
|
|
48
|
-
worker = PullWorker.new(Lims::Api::WsApi.new(Lab::Lims::Config.updates_socket))
|
|
48
|
+
worker = PullWorker.new(Lims::Api::WsApi.new(Lab::Lims::Config.updates_socket), start_date: start_date)
|
|
49
49
|
|
|
50
50
|
worker.pull_orders
|
|
51
51
|
end
|
|
@@ -300,6 +300,7 @@ module Lab
|
|
|
300
300
|
|
|
301
301
|
concept = params.dig(:specimen, :concept)
|
|
302
302
|
concept ||= params.dig(:specimen, :concept_id)
|
|
303
|
+
concept ||= unknown_concept_id
|
|
303
304
|
|
|
304
305
|
order_type = nil
|
|
305
306
|
order_type = OrderType.find_by_order_type_id!(params[:order_type_id])&.id if params[:order_type_id].present?
|
|
@@ -401,7 +402,7 @@ module Lab
|
|
|
401
402
|
end
|
|
402
403
|
|
|
403
404
|
def unknown_concept_id
|
|
404
|
-
ConceptName.find_by_name!('Unknown').
|
|
405
|
+
ConceptName.find_by_name!('Unknown').concept_id
|
|
405
406
|
end
|
|
406
407
|
|
|
407
408
|
def update_reason_for_test(order, concept_id, force_update: false)
|
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.2.1
|
|
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-03-
|
|
11
|
+
date: 2026-03-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: couchrest
|
|
@@ -341,9 +341,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
341
341
|
version: '0'
|
|
342
342
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
343
343
|
requirements:
|
|
344
|
-
- - "
|
|
344
|
+
- - ">="
|
|
345
345
|
- !ruby/object:Gem::Version
|
|
346
|
-
version:
|
|
346
|
+
version: '0'
|
|
347
347
|
requirements: []
|
|
348
348
|
rubygems_version: 3.4.1
|
|
349
349
|
signing_key:
|