his_emr_api_lab 0.0.3 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/lab/labels_controller.rb +2 -0
- data/app/services/lab/labelling_service/order_label.rb +20 -2
- data/app/services/lab/lims/api.rb +0 -5
- data/app/services/lab/lims/migrator.rb +14 -21
- data/app/services/lab/lims/order_dto.rb +4 -2
- 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 +29 -9
- data/app/services/lab/orders_search_service.rb +26 -16
- data/lib/couch_bum/couch_bum.rb +10 -5
- data/lib/generators/lab/install/templates/swagger.yaml +32 -0
- 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: 7b410e215fad55950c150d2ec7a82bda3f434cca4cc287e0d4fca2a9116e9182
|
4
|
+
data.tar.gz: 6b3a0c75c303c2139c1f6f963875667c4606bce42d23f6a22c277412e24db960
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f6068aecbc02d5c3df75ac422c4d4f9baeac86c6559a1e75078ef68088b4e6702239a366fb5e5879a2925592e3440a2880668bcc50f4730c9d2051f522ac94a
|
7
|
+
data.tar.gz: 5e3b4d61f507f2de7847951dcdd738a50dd29ba4ae5f981ce8284f3ba11c35d6d1c337787c1b78154b96eb318fc5343d404a3827fd02c89ffc3da60eb2d617c3
|
@@ -27,7 +27,7 @@ module Lab
|
|
27
27
|
'',
|
28
28
|
drawer,
|
29
29
|
'',
|
30
|
-
|
30
|
+
tests,
|
31
31
|
reason_for_test,
|
32
32
|
order.accession_number,
|
33
33
|
order.accession_number)
|
@@ -36,7 +36,7 @@ module Lab
|
|
36
36
|
def reason_for_test
|
37
37
|
return 'Unknown' unless order.reason_for_test
|
38
38
|
|
39
|
-
|
39
|
+
short_concept_name(order.reason_for_test.value_coded) || 'Unknown'
|
40
40
|
end
|
41
41
|
|
42
42
|
def patient
|
@@ -73,6 +73,24 @@ module Lab
|
|
73
73
|
ConceptName.find_by_concept_id(order.concept_id)&.name || 'Unknown'
|
74
74
|
end
|
75
75
|
|
76
|
+
def tests
|
77
|
+
tests = order.tests.map do |test|
|
78
|
+
name = short_concept_name(test.value_coded) || 'Unknown'
|
79
|
+
|
80
|
+
next 'VL' if name.match?(/Viral load/i)
|
81
|
+
|
82
|
+
name.size > 7 ? name[0..6] : name
|
83
|
+
end
|
84
|
+
|
85
|
+
tests.join(', ')
|
86
|
+
end
|
87
|
+
|
88
|
+
def short_concept_name(concept_id)
|
89
|
+
ConceptName.where(concept_id: concept_id)
|
90
|
+
.min_by { |concept| concept.name.size }
|
91
|
+
&.name
|
92
|
+
end
|
93
|
+
|
76
94
|
def unknown_concept
|
77
95
|
ConceptName.find_by_name('Unknown')
|
78
96
|
end
|
@@ -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,12 +139,13 @@ 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
|
-
headers = ['Accession number', 'NHID', 'First name', 'Last name', 'Reason']
|
145
|
+
headers = ['doc_id', 'Accession number', 'NHID', 'First name', 'Last name', 'Reason']
|
155
146
|
rows = (rejections || []).map do |rejection|
|
156
147
|
[
|
148
|
+
rejection.order[:_id],
|
157
149
|
rejection.order[:tracking_number],
|
158
150
|
rejection.order[:patient][:id],
|
159
151
|
rejection.order[:patient][:first_name],
|
@@ -165,12 +157,13 @@ module Lab
|
|
165
157
|
save_csv(MIGRATION_REJECTIONS_CSV_PATH, headers: headers, rows: rows)
|
166
158
|
end
|
167
159
|
|
168
|
-
MIGRATION_FAILURES_CSV_PATH =
|
160
|
+
MIGRATION_FAILURES_CSV_PATH = LIMS_LOG_PATH.join('migration-failures.csv')
|
169
161
|
|
170
162
|
def self.export_failures
|
171
|
-
headers = ['Accession number', 'NHID', 'Reason', 'Difference']
|
163
|
+
headers = ['doc_id', 'Accession number', 'NHID', 'Reason', 'Difference']
|
172
164
|
rows = Lab::LimsFailedImport.all.map do |failure|
|
173
165
|
[
|
166
|
+
failure.lims_id,
|
174
167
|
failure.tracking_number,
|
175
168
|
failure.patient_nhid,
|
176
169
|
failure.reason,
|
@@ -181,7 +174,7 @@ module Lab
|
|
181
174
|
save_csv(MIGRATION_FAILURES_CSV_PATH, headers: headers, rows: rows)
|
182
175
|
end
|
183
176
|
|
184
|
-
MIGRATION_LOG_PATH =
|
177
|
+
MIGRATION_LOG_PATH = LIMS_LOG_PATH.join('migration.log')
|
185
178
|
|
186
179
|
def self.start_migration
|
187
180
|
log_dir = Rails.root.join('log/lims')
|
@@ -30,9 +30,9 @@ module Lab
|
|
30
30
|
|
31
31
|
# Translates a LIMS specimen name to an OpenMRS concept_id
|
32
32
|
def specimen_type_id
|
33
|
-
lims_specimen_name = self['sample_type']
|
33
|
+
lims_specimen_name = self['sample_type']&.strip&.downcase
|
34
34
|
|
35
|
-
if %w[specimen_not_collected not_assigned].include?(lims_specimen_name)
|
35
|
+
if %w[specimen_not_collected not_assigned not_specified].include?(lims_specimen_name)
|
36
36
|
return ConceptName.select(:concept_id).find_by_name!('Unknown').concept_id
|
37
37
|
end
|
38
38
|
|
@@ -53,6 +53,8 @@ module Lab
|
|
53
53
|
|
54
54
|
# Extract requesting clinician name from LIMS
|
55
55
|
def requesting_clinician
|
56
|
+
return 'Unknown' unless self['who_order_test']
|
57
|
+
|
56
58
|
# TODO: Extend requesting clinician to an obs tree having extra parameters
|
57
59
|
# like phone number and ID to closely match the lims user.
|
58
60
|
first_name = self['who_order_test']['first_name'] || ''
|
@@ -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,21 @@ 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
|
+
# TODO: Verify that names being pushed to LIMS are of the correct format (ie matching
|
32
|
+
# LIMS naming conventions). Enable pushing when that is done
|
33
|
+
# worker.push_orders
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
18
37
|
def initialize(lims_api)
|
19
38
|
@lims_api = lims_api
|
20
39
|
end
|
@@ -52,8 +71,8 @@ module Lab
|
|
52
71
|
lims_api.update_order(mapping.lims_id, order_dto)
|
53
72
|
mapping.update(pushed_at: Time.now)
|
54
73
|
else
|
55
|
-
|
56
|
-
LimsOrderMapping.create!(order: order, lims_id:
|
74
|
+
update = lims_api.create_order(order_dto)
|
75
|
+
LimsOrderMapping.create!(order: order, lims_id: update['id'], revision: update['rev'], pushed_at: Time.now)
|
57
76
|
end
|
58
77
|
end
|
59
78
|
|
@@ -64,7 +83,8 @@ module Lab
|
|
64
83
|
# Pulls orders from the LIMS queue and writes them to the local database
|
65
84
|
def pull_orders
|
66
85
|
logger.info("Retrieving LIMS orders starting from #{last_seq}")
|
67
|
-
|
86
|
+
|
87
|
+
lims_api.consume_orders(from: last_seq, limit: 100) do |order_dto, context|
|
68
88
|
logger.debug("Retrieved order ##{order_dto[:tracking_number]}: #{order_dto}")
|
69
89
|
|
70
90
|
patient = find_patient_by_nhid(order_dto[:patient][:id])
|
@@ -80,6 +100,8 @@ module Lab
|
|
80
100
|
save_failed_import(order_dto, 'Demographics not matching', diff)
|
81
101
|
end
|
82
102
|
|
103
|
+
update_last_seq(context.current_seq)
|
104
|
+
|
83
105
|
[:accepted, "Patient NPID, '#{order_dto[:patient][:id]}', matched"]
|
84
106
|
rescue DuplicateNHID
|
85
107
|
logger.warn("Failed to import order due to duplicate patient NHID: #{order_dto[:patient][:id]}")
|
@@ -90,8 +112,6 @@ module Lab
|
|
90
112
|
rescue LimsException => e
|
91
113
|
logger.warn("Failed to import order due to #{e.class} - #{e.message}")
|
92
114
|
save_failed_import(order_dto, e.message)
|
93
|
-
ensure
|
94
|
-
update_last_seq(context.last_seq)
|
95
115
|
end
|
96
116
|
end
|
97
117
|
|
@@ -264,12 +284,12 @@ module Lab
|
|
264
284
|
indicator: { concept_id: indicator.concept_id },
|
265
285
|
value_type: value_type,
|
266
286
|
value: value_type == 'numeric' ? value.to_f : value,
|
267
|
-
value_modifier: value_modifier
|
287
|
+
value_modifier: value_modifier.blank? ? '=' : value_modifier
|
268
288
|
)
|
269
289
|
end
|
270
290
|
|
271
291
|
def parse_lims_result_value(value)
|
272
|
-
value = value['result_value']
|
292
|
+
value = value['result_value']&.strip
|
273
293
|
return nil, nil, nil if value.blank?
|
274
294
|
|
275
295
|
match = value&.match(/^(>|=|<|<=|>=)(.*)$/)
|
@@ -279,7 +299,7 @@ module Lab
|
|
279
299
|
end
|
280
300
|
|
281
301
|
def guess_result_datatype(result)
|
282
|
-
return 'numeric' if result.match?(/^[+-]?(\d+(\.\d+)|\.\d+)?$/)
|
302
|
+
return 'numeric' if result.strip.match?(/^[+-]?(\d+(\.\d+)|\.\d+)?$/)
|
283
303
|
|
284
304
|
'text'
|
285
305
|
end
|
@@ -303,7 +323,7 @@ module Lab
|
|
303
323
|
end
|
304
324
|
|
305
325
|
def last_seq_path
|
306
|
-
|
326
|
+
LIMS_LOG_PATH.join('last_seq.dat')
|
307
327
|
end
|
308
328
|
end
|
309
329
|
end
|
@@ -5,44 +5,54 @@ module Lab
|
|
5
5
|
module OrdersSearchService
|
6
6
|
class << self
|
7
7
|
def find_orders(filters)
|
8
|
-
|
9
|
-
status = filters.delete(:status)
|
8
|
+
extra_filters = pop_filters(filters, :date, :end_date, :status)
|
10
9
|
|
11
10
|
orders = Lab::LabOrder.prefetch_relationships
|
12
11
|
.where(filters)
|
13
12
|
.order(start_date: :desc)
|
14
13
|
|
15
|
-
orders =
|
16
|
-
orders =
|
14
|
+
orders = filter_orders_by_status(orders, pop_filters(extra_filters, :status))
|
15
|
+
orders = filter_orders_by_date(orders, extra_filters)
|
17
16
|
|
18
17
|
orders.map { |order| Lab::LabOrderSerializer.serialize_order(order) }
|
19
18
|
end
|
20
19
|
|
21
|
-
def filter_orders_by_date(orders, date)
|
22
|
-
|
23
|
-
|
20
|
+
def filter_orders_by_date(orders, date: nil, end_date: nil)
|
21
|
+
date = date&.to_date
|
22
|
+
end_date = end_date&.to_date
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
when 'ordered' then orders.where(concept_id: unknown_concept_id)
|
28
|
-
when 'drawn' then orders.where.not(concept_id: unknown_concept_id)
|
24
|
+
if date && end_date
|
25
|
+
return orders.where('start_date BETWEEN ? AND ?', date, end_date + 1.day)
|
29
26
|
end
|
30
|
-
end
|
31
27
|
|
32
|
-
|
33
|
-
|
28
|
+
if date
|
29
|
+
return orders.where('start_date BETWEEN ? AND ?', date, date + 1.day)
|
30
|
+
end
|
31
|
+
|
32
|
+
return orders.where('start_date < ?', end_date + 1.day) if end_date
|
33
|
+
|
34
|
+
orders
|
34
35
|
end
|
35
36
|
|
36
|
-
def filter_orders_by_status(orders, status)
|
37
|
-
case status
|
37
|
+
def filter_orders_by_status(orders, status: nil)
|
38
|
+
case status&.downcase
|
38
39
|
when 'ordered' then orders.where(concept_id: unknown_concept_id)
|
39
40
|
when 'drawn' then orders.where.not(concept_id: unknown_concept_id)
|
41
|
+
else orders
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
43
45
|
def unknown_concept_id
|
44
46
|
ConceptName.find_by_name!('Unknown').concept_id
|
45
47
|
end
|
48
|
+
|
49
|
+
def pop_filters(params, *filters)
|
50
|
+
filters.each_with_object({}) do |filter, popped_params|
|
51
|
+
next unless params.key?(filter)
|
52
|
+
|
53
|
+
popped_params[filter.to_sym] = params.delete(filter)
|
54
|
+
end
|
55
|
+
end
|
46
56
|
end
|
47
57
|
end
|
48
58
|
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
|
@@ -175,6 +175,12 @@ paths:
|
|
175
175
|
description: 'Filter by sample status: ordered, drawn'
|
176
176
|
schema:
|
177
177
|
type: string
|
178
|
+
- name: end_date
|
179
|
+
in: query
|
180
|
+
required: false
|
181
|
+
description: Select all results before this date
|
182
|
+
schema:
|
183
|
+
type: date
|
178
184
|
responses:
|
179
185
|
'200':
|
180
186
|
description: Success
|
@@ -396,6 +402,32 @@ paths:
|
|
396
402
|
responses:
|
397
403
|
'204':
|
398
404
|
description: No Content
|
405
|
+
"/api/v1/lab/reasons_for_test":
|
406
|
+
get:
|
407
|
+
summary: Reasons for test
|
408
|
+
description: Retrieve default reasons for test concept set
|
409
|
+
tags:
|
410
|
+
- Concepts
|
411
|
+
security:
|
412
|
+
- api_key: []
|
413
|
+
responses:
|
414
|
+
'200':
|
415
|
+
description: Success
|
416
|
+
content:
|
417
|
+
application/json:
|
418
|
+
schema:
|
419
|
+
type: array
|
420
|
+
items:
|
421
|
+
type: object
|
422
|
+
properties:
|
423
|
+
concept_id:
|
424
|
+
type: integer
|
425
|
+
name:
|
426
|
+
type: string
|
427
|
+
example: Routine
|
428
|
+
required:
|
429
|
+
- concept_id
|
430
|
+
- name
|
399
431
|
"/api/v1/lab/tests/{test_id}/results":
|
400
432
|
post:
|
401
433
|
summary: Add results to order
|
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.8
|
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-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: couchrest
|