his_emr_api_lab 0.0.3 → 0.0.4
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/services/lab/lims/api.rb +0 -5
- data/app/services/lab/lims/migrator.rb +10 -19
- data/app/services/lab/lims/order_serializer.rb +3 -1
- data/app/services/lab/lims/utils.rb +12 -0
- data/app/services/lab/lims/worker.rb +24 -6
- data/lib/couch_bum/couch_bum.rb +10 -5
- data/lib/lab/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f53ca7acc69718ba31cf2ae278b6fdbdc4996d3ad6a28db0a08d3945d1463998
|
4
|
+
data.tar.gz: 77b2cfdc430706b9864e9ec71b2f4b64c2ec11a5695b45b81984c86a8fefefd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19f53f3c471dd4552f64bf1200a6bbd8051e7bc7bc7e0670b2079bea4af61621575a9c0e7f5cdc06c43ac7b80c2534e6ca704fd06e43b0deed3475ea66690017
|
7
|
+
data.tar.gz: cbe4e02964f635b60e7e7b0c6b53ac3b406ab9bff55da582371a3de38a8d65901bcca1771a7b3cf0d8be83e31ac8bf0a15e9c752f7131bb631f1011df95fa713
|
@@ -29,16 +29,11 @@ module Lab
|
|
29
29
|
# given block until the queue is empty or connection is terminated
|
30
30
|
# by calling method +choke+.
|
31
31
|
def consume_orders(from: 0, limit: 30)
|
32
|
-
last_seq = { value: 0 }
|
33
|
-
|
34
32
|
bum.binge_changes(since: from, limit: limit, include_docs: true) do |change|
|
35
33
|
next unless change['doc']['type']&.casecmp?('Order')
|
36
34
|
|
37
35
|
yield OrderDTO.new(change['doc']), self
|
38
|
-
last_seq[:value] = self.last_seq
|
39
36
|
end
|
40
|
-
|
41
|
-
last_seq[:value]
|
42
37
|
end
|
43
38
|
|
44
39
|
def create_order(order)
|
@@ -29,6 +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 './worker'
|
32
33
|
require_relative '../orders_service'
|
33
34
|
require_relative '../results_service'
|
34
35
|
require_relative '../tests_service'
|
@@ -47,14 +48,16 @@ module Lab
|
|
47
48
|
|
48
49
|
attr_reader :rejections
|
49
50
|
|
50
|
-
def consume_orders(from: nil,
|
51
|
+
def consume_orders(from: nil, **_kwargs)
|
52
|
+
limit = 50_000
|
53
|
+
|
51
54
|
Parallel.each(read_orders(from, limit),
|
52
55
|
in_processes: MAX_THREADS,
|
53
56
|
finish: order_pmap_post_processor(from)) do |row|
|
54
57
|
next unless row['doc']['type']&.casecmp?('Order')
|
55
58
|
|
56
|
-
User.current =
|
57
|
-
yield OrderDTO.new(row['doc']), OpenStruct.new(last_seq: from)
|
59
|
+
User.current = Utils.lab_user
|
60
|
+
yield OrderDTO.new(row['doc']), OpenStruct.new(last_seq: (from || 0) + limit, current_seq: from)
|
58
61
|
end
|
59
62
|
end
|
60
63
|
|
@@ -70,7 +73,7 @@ module Lab
|
|
70
73
|
private
|
71
74
|
|
72
75
|
def last_seq_path
|
73
|
-
|
76
|
+
LIMS_LOG_PATH.join('migration-last-id.dat')
|
74
77
|
end
|
75
78
|
|
76
79
|
def order_pmap_post_processor(last_seq)
|
@@ -129,18 +132,6 @@ module Lab
|
|
129
132
|
def update_last_seq(_last_seq); end
|
130
133
|
end
|
131
134
|
|
132
|
-
def self.lab_user
|
133
|
-
user = User.find_by_username('lab_daemon')
|
134
|
-
return user if user
|
135
|
-
|
136
|
-
god_user = User.first
|
137
|
-
|
138
|
-
person = Person.create!(creator: god_user.user_id)
|
139
|
-
PersonName.create!(person: person, given_name: 'Lab', family_name: 'Daemon', creator: god_user.user_id)
|
140
|
-
|
141
|
-
User.create!(username: 'lab_daemon', person: person, creator: god_user.user_id)
|
142
|
-
end
|
143
|
-
|
144
135
|
def self.save_csv(filename, rows:, headers: nil)
|
145
136
|
CSV.open(filename, File::WRONLY | File::CREAT) do |csv|
|
146
137
|
csv << headers if headers
|
@@ -148,7 +139,7 @@ module Lab
|
|
148
139
|
end
|
149
140
|
end
|
150
141
|
|
151
|
-
MIGRATION_REJECTIONS_CSV_PATH =
|
142
|
+
MIGRATION_REJECTIONS_CSV_PATH = LIMS_LOG_PATH.join('migration-rejections.csv')
|
152
143
|
|
153
144
|
def self.export_rejections(rejections)
|
154
145
|
headers = ['Accession number', 'NHID', 'First name', 'Last name', 'Reason']
|
@@ -165,7 +156,7 @@ module Lab
|
|
165
156
|
save_csv(MIGRATION_REJECTIONS_CSV_PATH, headers: headers, rows: rows)
|
166
157
|
end
|
167
158
|
|
168
|
-
MIGRATION_FAILURES_CSV_PATH =
|
159
|
+
MIGRATION_FAILURES_CSV_PATH = LIMS_LOG_PATH.join('migration-failures.csv')
|
169
160
|
|
170
161
|
def self.export_failures
|
171
162
|
headers = ['Accession number', 'NHID', 'Reason', 'Difference']
|
@@ -181,7 +172,7 @@ module Lab
|
|
181
172
|
save_csv(MIGRATION_FAILURES_CSV_PATH, headers: headers, rows: rows)
|
182
173
|
end
|
183
174
|
|
184
|
-
MIGRATION_LOG_PATH =
|
175
|
+
MIGRATION_LOG_PATH = LIMS_LOG_PATH.join('migration.log')
|
185
176
|
|
186
177
|
def self.start_migration
|
187
178
|
log_dir = Rails.root.join('log/lims')
|
@@ -97,7 +97,9 @@ module Lab
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def format_test_results(order)
|
100
|
-
order.tests
|
100
|
+
order.tests&.each_with_object({}) do |test, results|
|
101
|
+
next unless test.result
|
102
|
+
|
101
103
|
results[test.name] = {
|
102
104
|
results: test.result.each_with_object({}) do |measure, measures|
|
103
105
|
measures[measure.indicator.name] = { result_value: "#{measure.value_modifier}#{measure.value}" }
|
@@ -39,6 +39,18 @@ module Lab
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
+
def self.lab_user
|
43
|
+
user = User.find_by_username('lab_daemon')
|
44
|
+
return user if user
|
45
|
+
|
46
|
+
god_user = User.first
|
47
|
+
|
48
|
+
person = Person.create!(creator: god_user.user_id)
|
49
|
+
PersonName.create!(person: person, given_name: 'Lab', family_name: 'Daemon', creator: god_user.user_id)
|
50
|
+
|
51
|
+
User.create!(username: 'lab_daemon', person: person, creator: god_user.user_id)
|
52
|
+
end
|
53
|
+
|
42
54
|
def self.parse_date(str_date, fallback_date = nil)
|
43
55
|
if str_date.blank? && fallback_date.blank?
|
44
56
|
raise "Can't parse blank date"
|
@@ -2,12 +2,16 @@
|
|
2
2
|
|
3
3
|
require 'cgi/util'
|
4
4
|
|
5
|
+
require_relative './api'
|
5
6
|
require_relative './exceptions'
|
6
7
|
require_relative './order_serializer'
|
7
8
|
require_relative './utils'
|
8
9
|
|
9
10
|
module Lab
|
10
11
|
module Lims
|
12
|
+
LIMS_LOG_PATH = Rails.root.join('log/lims')
|
13
|
+
Dir.mkdir(LIMS_LOG_PATH) unless File.exist?(LIMS_LOG_PATH)
|
14
|
+
|
11
15
|
##
|
12
16
|
# Pull/Push orders from/to the LIMS queue (Oops meant CouchDB).
|
13
17
|
class Worker
|
@@ -15,6 +19,19 @@ module Lab
|
|
15
19
|
|
16
20
|
attr_reader :lims_api
|
17
21
|
|
22
|
+
def self.start
|
23
|
+
File.open(LIMS_LOG_PATH.join('worker.lock'), File::WRONLY | File::CREAT, 0o644) do |fout|
|
24
|
+
fout.flock(File::LOCK_EX)
|
25
|
+
|
26
|
+
User.current = Utils.lab_user
|
27
|
+
|
28
|
+
fout.write("Worker ##{Process.pid} started at #{Time.now}")
|
29
|
+
worker = new(Api.new)
|
30
|
+
worker.pull_orders
|
31
|
+
worker.push_orders
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
18
35
|
def initialize(lims_api)
|
19
36
|
@lims_api = lims_api
|
20
37
|
end
|
@@ -52,8 +69,8 @@ module Lab
|
|
52
69
|
lims_api.update_order(mapping.lims_id, order_dto)
|
53
70
|
mapping.update(pushed_at: Time.now)
|
54
71
|
else
|
55
|
-
|
56
|
-
LimsOrderMapping.create!(order: order, lims_id:
|
72
|
+
update = lims_api.create_order(order_dto)
|
73
|
+
LimsOrderMapping.create!(order: order, lims_id: update['id'], revision: update['rev'], pushed_at: Time.now)
|
57
74
|
end
|
58
75
|
end
|
59
76
|
|
@@ -64,7 +81,8 @@ module Lab
|
|
64
81
|
# Pulls orders from the LIMS queue and writes them to the local database
|
65
82
|
def pull_orders
|
66
83
|
logger.info("Retrieving LIMS orders starting from #{last_seq}")
|
67
|
-
|
84
|
+
|
85
|
+
lims_api.consume_orders(from: last_seq, limit: 100) do |order_dto, context|
|
68
86
|
logger.debug("Retrieved order ##{order_dto[:tracking_number]}: #{order_dto}")
|
69
87
|
|
70
88
|
patient = find_patient_by_nhid(order_dto[:patient][:id])
|
@@ -80,6 +98,8 @@ module Lab
|
|
80
98
|
save_failed_import(order_dto, 'Demographics not matching', diff)
|
81
99
|
end
|
82
100
|
|
101
|
+
update_last_seq(context.current_seq)
|
102
|
+
|
83
103
|
[:accepted, "Patient NPID, '#{order_dto[:patient][:id]}', matched"]
|
84
104
|
rescue DuplicateNHID
|
85
105
|
logger.warn("Failed to import order due to duplicate patient NHID: #{order_dto[:patient][:id]}")
|
@@ -90,8 +110,6 @@ module Lab
|
|
90
110
|
rescue LimsException => e
|
91
111
|
logger.warn("Failed to import order due to #{e.class} - #{e.message}")
|
92
112
|
save_failed_import(order_dto, e.message)
|
93
|
-
ensure
|
94
|
-
update_last_seq(context.last_seq)
|
95
113
|
end
|
96
114
|
end
|
97
115
|
|
@@ -303,7 +321,7 @@ module Lab
|
|
303
321
|
end
|
304
322
|
|
305
323
|
def last_seq_path
|
306
|
-
|
324
|
+
LIMS_LOG_PATH.join('last_seq.dat')
|
307
325
|
end
|
308
326
|
end
|
309
327
|
end
|
data/lib/couch_bum/couch_bum.rb
CHANGED
@@ -24,18 +24,21 @@ class CouchBum
|
|
24
24
|
# within the passed block.
|
25
25
|
def binge_changes(since: 0, limit: nil, include_docs: nil, &block)
|
26
26
|
catch(:choke) do
|
27
|
-
|
27
|
+
logger.debug("Binging #{limit} changes from '#{since}'")
|
28
|
+
params = stringify_params(limit: limit, include_docs: include_docs)
|
29
|
+
params = "since=#{since}&#{params}" unless since.blank?
|
28
30
|
|
29
|
-
changes = couch_rest(:get, "_changes
|
31
|
+
changes = couch_rest(:get, "_changes?#{params}")
|
30
32
|
context = BingeContext.new(changes)
|
31
|
-
changes['results'].each
|
33
|
+
changes['results'].each do |change|
|
34
|
+
context.current_seq = change['seq']
|
35
|
+
context.instance_exec(change, &block)
|
36
|
+
end
|
32
37
|
end
|
33
38
|
end
|
34
39
|
|
35
40
|
def couch_rest(method, route, *args, **kwargs)
|
36
41
|
url = expand_route(route)
|
37
|
-
|
38
|
-
logger.debug("CouchBum: Executing #{method} #{url}")
|
39
42
|
CouchRest.send(method, url, *args, **kwargs)
|
40
43
|
rescue CouchRest::Exception => e
|
41
44
|
logger.error("Failed to communicate with CouchDB: Status: #{e.http_code} - #{e.http_body}")
|
@@ -46,6 +49,8 @@ class CouchBum
|
|
46
49
|
|
47
50
|
# Context under which the callback passed to binge_changes is executed.
|
48
51
|
class BingeContext
|
52
|
+
attr_accessor :current_seq
|
53
|
+
|
49
54
|
def initialize(changes)
|
50
55
|
@changes = changes
|
51
56
|
end
|
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: 0.0.
|
4
|
+
version: 0.0.4
|
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: 2021-04-
|
11
|
+
date: 2021-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: couchrest
|