his_emr_api_lab 2.4.5 → 2.4.6

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: ea9b8787072da5656c0ff4f6a2e42fa83681842bec516d07af6634798ccdb104
4
- data.tar.gz: 9c691c8f25fffc12c055481698c90817f12ea667c622652cb55d9f74741c848d
3
+ metadata.gz: 2ac3fb7e82f896ac9c5f0545aa1c7e97290e7aaf11cc149fd6e8f385938397dc
4
+ data.tar.gz: 707411853618c5b391c99ecdc043bdbc71ef5df5a1b801567869d279b068c6d7
5
5
  SHA512:
6
- metadata.gz: 2799cfe52b7a0f01f9e07fa61fdf1a1355b8ddad2c2f6642bc731b82e25337936c1cea8c4100000e9787268ffd43504df2642faaf4a48281299403f1311692a3
7
- data.tar.gz: a97f6b54b8a78a07a41740c01192d73ea779236ba9caeb34c5b884dd61cc74ac1256f5a728a64f2c76bdd3a260644443cd20af46ffb2b749473b715a0228cc06
6
+ metadata.gz: e0ae79db2d3cb66121a3daf92c1fb0e141a09309dc148f604da49cb167203fe22c77e50c843a9a10e6531fe505cb8241c5212668a643bff38ef5f851d8232a72
7
+ data.tar.gz: 32ec983d0e45aa6af8b280ae1850544ad42bb250465cdfa45a597818e589c3ff9238d7ebea7ba7cdc7c613ae4f2368fc05c2c854809fa2f1d2d8b76ed82c1bcf
@@ -14,11 +14,13 @@ module Lab
14
14
  date_received: params[:date_received])
15
15
  end
16
16
 
17
- def acknowledgements_pending_sync(batch_size, start_date: nil, last_order_id: 0)
17
+ def acknowledgements_pending_sync(batch_size, start_date: nil, last_order_id: 0, accession_numbers: [], patient_id: nil)
18
18
  query = Lab::LabAcknowledgement.joins(:order).where(pushed: false)
19
19
  .where('lims_acknowledgement_statuses.order_id > ?', last_order_id)
20
20
  query = query.where('orders.date_created >= ?', start_date) if start_date
21
- query.order('lims_acknowledgement_statuses.order_id ASC').limit(batch_size)
21
+ query = query.where(orders: {accession_number: accession_numbers}) if accession_numbers.any?
22
+ query = query.where(orders: {patient_id: patient_id}) if patient_id
23
+ query.order('lims_acknowledgement_statuses.order_id DESC').limit(batch_size)
22
24
  end
23
25
 
24
26
  def push_acknowledgement(acknowledgement, lims_api)
@@ -4,15 +4,17 @@ 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, :start_date
7
+ attr_reader :lims_api, :start_date, :accession_numbers, :patient_id
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, start_date: nil)
13
+ def initialize(lims_api, start_date: nil, accession_numbers: [], patient_id: nil)
14
14
  @lims_api = lims_api
15
15
  @start_date = start_date
16
+ @accession_numbers = accession_numbers
17
+ @patient_id = patient_id
16
18
  end
17
19
 
18
20
  def push_acknowledgement(batch_size: 1000, wait: false)
@@ -21,6 +23,8 @@ module Lab
21
23
  logger.info('Looking for new acknowledgements to push to LIMS...')
22
24
  acknowledgements = Lab::AcknowledgementService.acknowledgements_pending_sync(batch_size,
23
25
  start_date: start_date,
26
+ accession_numbers: accession_numbers,
27
+ patient_id: patient_id,
24
28
  last_order_id: last_order_id).all
25
29
 
26
30
  logger.debug("Found #{acknowledgements.size} acknowledgements...")
@@ -75,8 +75,8 @@ module Lab
75
75
  { tracking_number: order_dto[:tracking_number] }
76
76
  end
77
77
 
78
- def consume_orders(*_args, patient_id: nil, start_date: nil, **_kwargs)
79
- orders_pending_updates(patient_id, start_date: start_date).each do |order|
78
+ def consume_orders(*_args, patient_id: nil, start_date: nil, accession_numbers: [], **_kwargs)
79
+ orders_pending_updates(patient_id, start_date: start_date, accession_numbers: accession_numbers).each do |order|
80
80
  order_dto = Lab::Lims::OrderSerializer.serialize_order(order)
81
81
 
82
82
  # Always fetch the full order from NLIMS to get status trails
@@ -84,11 +84,6 @@ module Lab
84
84
  lims_order = find_lims_order(order.accession_number)
85
85
  patch_order_dto_with_lims_order!(order_dto, lims_order)
86
86
 
87
- Rails.logger.debug("NLIMS order structure for #{order.accession_number}:")
88
- Rails.logger.debug(" Has 'order' key: #{lims_order.key?('order')}")
89
- Rails.logger.debug(" Has 'data' key: #{lims_order.key?('data')}")
90
- Rails.logger.debug(" Top level keys: #{lims_order.keys.inspect}")
91
-
92
87
  # Also extract status trails from the NLIMS order
93
88
  # Note: NLIMS might return order data under 'order' or 'data.order'
94
89
  order_data = lims_order['order'] || lims_order.dig('data', 'order') || lims_order
@@ -606,20 +601,20 @@ module Lab
606
601
  }
607
602
  end
608
603
 
609
- def orders_pending_updates(patient_id = nil, start_date: nil)
604
+ def orders_pending_updates(patient_id = nil, start_date: nil, accession_numbers: [])
610
605
  Rails.logger.info("Looking for orders that need to be updated... [patient_id=#{patient_id}] [start_date=#{start_date}]")
611
606
  orders = {}
612
607
  Rails.logger.info("Fetching orders without specimen...")
613
- orders_without_specimen(patient_id, start_date: start_date).each { |order| orders[order.order_id] = order }
608
+ orders_without_specimen(patient_id, start_date: start_date, accession_numbers: accession_numbers).each { |order| orders[order.order_id] = order }
614
609
  Rails.logger.info("Fetching orders without results...")
615
- orders_without_results(patient_id, start_date: start_date).each { |order| orders[order.order_id] = order }
610
+ orders_without_results(patient_id, start_date: start_date, accession_numbers: accession_numbers).each { |order| orders[order.order_id] = order }
616
611
  Rails.logger.info("Fetching orders without reason...")
617
- orders_without_reason(patient_id, start_date: start_date).each { |order| orders[order.order_id] = order }
612
+ orders_without_reason(patient_id, start_date: start_date, accession_numbers: accession_numbers).each { |order| orders[order.order_id] = order }
618
613
 
619
614
  orders.values
620
615
  end
621
616
 
622
- def orders_without_specimen(patient_id = nil, start_date: nil)
617
+ def orders_without_specimen(patient_id = nil, start_date: nil, accession_numbers: [])
623
618
  Rails.logger.debug('Looking for orders without a specimen')
624
619
  unknown_specimen = ConceptName.where(name: Lab::Metadata::UNKNOWN_SPECIMEN)
625
620
  .select(:concept_id)
@@ -627,21 +622,23 @@ module Lab
627
622
  .where.not(accession_number: Lab::LimsOrderMapping.select(:lims_id))
628
623
  orders = orders.where(patient_id:) if patient_id
629
624
  orders = orders.where('orders.date_created >= ?', start_date) if start_date
625
+ orders = orders.where(accession_number: accession_numbers) if accession_numbers.any?
630
626
 
631
627
  orders
632
628
  end
633
629
 
634
- def orders_without_results(patient_id = nil, start_date: nil)
630
+ def orders_without_results(patient_id = nil, start_date: nil, accession_numbers: [])
635
631
  Rails.logger.debug('Looking for orders without a result')
636
632
  # Lab::OrdersSearchService.find_orders_without_results(patient_id: patient_id)
637
633
  # .where.not(accession_number: Lab::LimsOrderMapping.select(:lims_id).where("pulled_at IS NULL"))
638
634
  orders = Lab::OrdersSearchService.find_orders_without_results(patient_id:)
639
635
  .where(order_id: Lab::LimsOrderMapping.select(:order_id))
640
636
  orders = orders.where('orders.date_created >= ?', start_date) if start_date
637
+ orders = orders.where(accession_number: accession_numbers) if accession_numbers.any?
641
638
  orders
642
639
  end
643
640
 
644
- def orders_without_reason(patient_id = nil, start_date: nil)
641
+ def orders_without_reason(patient_id = nil, start_date: nil, accession_numbers: [])
645
642
  Rails.logger.debug('Looking for orders without a reason for test')
646
643
  orders = Lab::LabOrder.joins(:reason_for_test)
647
644
  .merge(Observation.where(value_coded: nil, value_text: nil))
@@ -649,6 +646,7 @@ module Lab
649
646
  .where.not(accession_number: Lab::LimsOrderMapping.select(:lims_id))
650
647
  orders = orders.where(patient_id:) if patient_id
651
648
  orders = orders.where('orders.date_created >= ?', start_date) if start_date
649
+ orders = orders.where(accession_number: accession_numbers) if accession_numbers.any?
652
650
 
653
651
  orders
654
652
  end
@@ -7,15 +7,17 @@ module Lab
7
7
  ##
8
8
  # Pulls orders from a Lims API object and saves them to the local database.
9
9
  class PullWorker
10
- attr_reader :lims_api, :start_date
10
+ attr_reader :lims_api, :start_date, :accession_numbers
11
11
 
12
12
  include Utils # for logger
13
13
 
14
14
  LIMS_LOG_PATH = Rails.root.join('log', 'lims')
15
15
 
16
- def initialize(lims_api, start_date: nil)
16
+ def initialize(lims_api, start_date: nil, accession_numbers: [], patient_id: nil)
17
17
  @lims_api = lims_api
18
18
  @start_date = start_date
19
+ @accession_numbers = accession_numbers
20
+ @patient_id = patient_id
19
21
  end
20
22
 
21
23
  ##
@@ -23,7 +25,7 @@ module Lab
23
25
  def pull_orders(batch_size: 10_000, **)
24
26
  logger.info("Retrieving LIMS orders starting from #{last_seq}")
25
27
 
26
- lims_api.consume_orders(from: last_seq, limit: batch_size, start_date: start_date, **) do |order_dto, context|
28
+ lims_api.consume_orders(from: last_seq, limit: batch_size, start_date: start_date, accession_numbers: accession_numbers, **) do |order_dto, context|
27
29
  logger.debug("Retrieved order ##{order_dto[:tracking_number]}: #{order_dto}")
28
30
 
29
31
  patient = find_patient_by_nhid(order_dto[:patient][:id], order_dto[:tracking_number])
@@ -395,7 +397,7 @@ module Lab
395
397
  value_text: status_data['status'], # Store status as text
396
398
  obs_datetime: timestamp,
397
399
  comments: updated_by.to_json,
398
- creator: User.current&.user_id || 1,
400
+ creator: User.current&.user_id,
399
401
  location_id:,
400
402
  date_created: Time.now,
401
403
  uuid: SecureRandom.uuid
@@ -463,7 +465,7 @@ module Lab
463
465
  value_text: status_data['status'], # Store status as text
464
466
  obs_datetime: timestamp,
465
467
  comments: updated_by.to_json,
466
- creator: User.current&.user_id || 1,
468
+ creator: User.current&.user_id,
467
469
  location_id:,
468
470
  date_created: Time.now,
469
471
  uuid: SecureRandom.uuid
@@ -5,16 +5,18 @@ module Lab
5
5
  ##
6
6
  # Pushes all local orders to a LIMS Api object.
7
7
  class PushWorker
8
- attr_reader :lims_api, :start_date
8
+ attr_reader :lims_api, :start_date, :accession_numbers, :patient_id
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, start_date: nil)
15
+ def initialize(lims_api, start_date: nil, accession_numbers: [], patient_id: nil)
16
16
  @lims_api = lims_api
17
17
  @start_date = start_date
18
+ @accession_numbers = accession_numbers
19
+ @patient_id = patient_id
18
20
  end
19
21
 
20
22
  def push_orders(batch_size: 1000, wait: false)
@@ -109,7 +111,8 @@ module Lab
109
111
  else
110
112
  query.where('orders.date_created >= ? AND orders.date_created <= ?', START_DATE, Date.today + 1.day)
111
113
  end
112
-
114
+ query = query.where(accession_number: accession_numbers) if accession_numbers.any?
115
+ query = query.where(patient_id: patient_id) if patient_id
113
116
  query.order(date_created: :desc)
114
117
  end
115
118
 
@@ -130,11 +133,14 @@ module Lab
130
133
 
131
134
  def voided_orders
132
135
  Rails.logger.debug('Looking for voided orders that are being tracked by LIMS...')
133
- Lab::LabOrder.unscoped
136
+ orders = Lab::LabOrder.unscoped
134
137
  .where(order_type: OrderType.where(name: Lab::Metadata::ORDER_TYPE_NAME),
135
138
  order_id: Lab::LimsOrderMapping.all.select(:order_id),
136
139
  voided: 1)
137
- .order(date_voided: :desc)
140
+ orders = orders.where(accession_number: accession_numbers) if accession_numbers.any?
141
+ orders = orders.where(patient_id: patient_id) if patient_id
142
+ orders = orders.order(date_voided: :desc)
143
+ orders
138
144
  end
139
145
 
140
146
  ##
@@ -9,46 +9,46 @@ 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(start_date: nil)
12
+ def self.start(start_date: nil, accession_numbers: [], patient_id: nil)
13
13
  User.current = Utils.lab_user
14
14
 
15
15
  # Eager load classes before forking to avoid autoloading issues in child processes
16
16
  Rails.application.eager_load! if defined?(Rails) && Rails.env.development?
17
17
 
18
- fork { start_push_worker(start_date: start_date) }
19
- fork { start_pull_worker(start_date: start_date) }
20
- fork { start_acknowledgement_worker(start_date: start_date) }
21
- fork { start_realtime_pull_worker(start_date: start_date) } if realtime_updates_enabled?
18
+ fork { start_push_worker(start_date: start_date, accession_numbers: accession_numbers, patient_id: patient_id) }
19
+ fork { start_pull_worker(start_date: start_date, accession_numbers: accession_numbers, patient_id: patient_id) }
20
+ fork { start_acknowledgement_worker(start_date: start_date, accession_numbers: accession_numbers, patient_id: patient_id) }
21
+ fork { start_realtime_pull_worker(start_date: start_date, accession_numbers: accession_numbers, patient_id: patient_id) } if realtime_updates_enabled?
22
22
 
23
23
  Process.waitall
24
24
  end
25
25
 
26
- def self.start_push_worker(start_date: nil)
27
- start_worker('push_worker') do
28
- worker = PushWorker.new(lims_api, start_date: start_date)
26
+ def self.start_push_worker(start_date: nil, accession_numbers: [], patient_id: nil)
27
+ start_worker('push_worker', accession_numbers: accession_numbers, patient_id: patient_id) do
28
+ worker = PushWorker.new(lims_api, start_date: start_date, accession_numbers: accession_numbers, patient_id: patient_id)
29
29
 
30
30
  worker.push_orders # (wait: true)
31
31
  end
32
32
  end
33
33
 
34
- def self.start_acknowledgement_worker(start_date: nil)
35
- start_worker('acknowledgement_worker') do
36
- worker = AcknowledgementWorker.new(lims_api, start_date: start_date)
34
+ def self.start_acknowledgement_worker(start_date: nil, accession_numbers: [], patient_id: nil)
35
+ start_worker('acknowledgement_worker', accession_numbers: accession_numbers, patient_id: patient_id) do
36
+ worker = AcknowledgementWorker.new(lims_api, start_date: start_date, accession_numbers: accession_numbers, patient_id: patient_id)
37
37
  worker.push_acknowledgement
38
38
  end
39
39
  end
40
40
 
41
- def self.start_pull_worker(start_date: nil)
42
- start_worker('pull_worker') do
43
- worker = PullWorker.new(lims_api, start_date: start_date)
41
+ def self.start_pull_worker(start_date: nil, accession_numbers: [], patient_id: nil)
42
+ start_worker('pull_worker', accession_numbers: accession_numbers, patient_id: patient_id) do
43
+ worker = PullWorker.new(lims_api, start_date: start_date, accession_numbers: accession_numbers, patient_id: patient_id)
44
44
 
45
45
  worker.pull_orders
46
46
  end
47
47
  end
48
48
 
49
- def self.start_realtime_pull_worker(start_date: nil)
50
- start_worker('realtime_pull_worker') do
51
- worker = PullWorker.new(Lims::Api::WsApi.new(Lab::Lims::Config.updates_socket), start_date: start_date)
49
+ def self.start_realtime_pull_worker(start_date: nil, accession_numbers: [], patient_id: nil)
50
+ start_worker('realtime_pull_worker', accession_numbers: accession_numbers, patient_id: patient_id) do
51
+ worker = PullWorker.new(Lims::Api::WsApi.new(Lab::Lims::Config.updates_socket), start_date: start_date, accession_numbers: accession_numbers, patient_id: patient_id)
52
52
 
53
53
  worker.pull_orders
54
54
  end
@@ -57,7 +57,8 @@ module Lab
57
57
  LOG_FILES_TO_KEEP = 5
58
58
  LOG_FILE_SIZE = 500.megabytes
59
59
 
60
- def self.start_worker(worker_name)
60
+ def self.start_worker(worker_name, accession_numbers: [], patient_id: nil)
61
+ worker_name = create_file_name(worker_name, accession_numbers: accession_numbers, patient_id: patient_id)
61
62
  Rails.logger = LoggerMultiplexor.new(file_logger(worker_name), $stdout)
62
63
  ActiveRecord::Base.logger = Rails.logger
63
64
  Rails.logger.level = :debug
@@ -78,6 +79,15 @@ module Lab
78
79
  Logger.new(log_path("#{worker_name}.log"), LOG_FILES_TO_KEEP, LOG_FILE_SIZE)
79
80
  end
80
81
 
82
+ def self.create_file_name(worker_name, accession_numbers: [], patient_id: nil)
83
+ accession_numbers = accession_numbers.nil? ? [] : accession_numbers
84
+ accession_numbers = accession_numbers.map { |accession| accession.gsub(/[^a-zA-Z0-9]/, '') }.join('_')
85
+ filename = "#{worker_name}"
86
+ filename += "_#{accession_numbers}" unless accession_numbers.empty?
87
+ filename += "_#{patient_id}" if patient_id
88
+ filename
89
+ end
90
+
81
91
  def self.log_path(filename)
82
92
  Lab::Lims::Utils::LIMS_LOG_PATH.join(filename)
83
93
  end
@@ -600,7 +600,7 @@ module Lab
600
600
  value_text: status, # Store status as text
601
601
  obs_datetime: timestamp,
602
602
  comments: updated_by.to_json,
603
- creator: User.current&.user_id || 1,
603
+ creator: User.current&.user_id,
604
604
  location_id:,
605
605
  date_created: Time.now,
606
606
  uuid: SecureRandom.uuid
@@ -641,7 +641,7 @@ module Lab
641
641
  value_text: status, # Store status as text
642
642
  obs_datetime: timestamp,
643
643
  comments: updated_by.to_json,
644
- creator: User.current&.user_id || 1,
644
+ creator: User.current&.user_id,
645
645
  location_id:,
646
646
  date_created: Time.now,
647
647
  uuid: SecureRandom.uuid
@@ -141,7 +141,7 @@ module Lab
141
141
  'id' => User.current&.user_id&.to_s,
142
142
  'phone_number' => nil
143
143
  }.to_json,
144
- creator: User.current&.user_id || 1,
144
+ creator: User.current&.user_id,
145
145
  location_id:,
146
146
  date_created: Time.now,
147
147
  uuid: SecureRandom.uuid
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.4.5'.freeze
4
+ VERSION = '2.4.6'.freeze
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.4.5
4
+ version: 2.4.6
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-09-18 00:00:00.000000000 Z
11
+ date: 2026-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: couchrest