his_emr_api_lab 2.1.8.7 → 2.1.9.pre.alpha
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 +4 -22
- data/app/models/lab/lab_order.rb +23 -8
- data/app/models/lab/lab_result.rb +2 -2
- data/app/models/lab/lab_test.rb +21 -2
- data/app/serializers/lab/lab_order_serializer.rb +77 -7
- data/app/serializers/lab/result_serializer.rb +2 -2
- data/app/services/lab/accession_number_service.rb +2 -2
- data/app/services/lab/acknowledgement_service.rb +4 -8
- data/app/services/lab/lims/acknowledgement_worker.rb +3 -5
- data/app/services/lab/lims/api/rest_api.rb +100 -25
- data/app/services/lab/lims/order_serializer.rb +0 -1
- data/app/services/lab/lims/pull_worker.rb +172 -4
- data/app/services/lab/lims/push_worker.rb +15 -26
- data/app/services/lab/lims/worker.rb +13 -13
- data/app/services/lab/metadata.rb +1 -0
- data/app/services/lab/orders_service.rb +223 -24
- data/app/services/lab/results_service.rb +1 -5
- data/app/services/lab/tests_service.rb +48 -4
- data/db/migrate/20260226065149_create_lab_status_concepts.rb +80 -0
- data/lib/lab/version.rb +1 -1
- metadata +5 -4
|
@@ -60,8 +60,14 @@ module Lab
|
|
|
60
60
|
|
|
61
61
|
attach_test_method(order, order_params) if order_params[:test_method]
|
|
62
62
|
|
|
63
|
+
# Create initial order status trail
|
|
64
|
+
create_initial_order_status_trail(order)
|
|
65
|
+
|
|
63
66
|
Lab::TestsService.create_tests(order, order_params[:date], order_params[:tests])
|
|
64
67
|
|
|
68
|
+
# Reload order to include status trails and tests
|
|
69
|
+
order = Lab::LabOrder.prefetch_relationships.find(order.order_id)
|
|
70
|
+
|
|
65
71
|
Lab::LabOrderSerializer.serialize_order(
|
|
66
72
|
order, requesting_clinician: add_requesting_clinician(order, order_params),
|
|
67
73
|
reason_for_test: add_reason_for_test(order, order_params),
|
|
@@ -102,14 +108,16 @@ module Lab
|
|
|
102
108
|
|
|
103
109
|
if reason_for_test
|
|
104
110
|
Rails.logger.debug("Updating reason for test on order ##{order.order_id}")
|
|
105
|
-
update_reason_for_test(order, Concept.find(reason_for_test)&.id,
|
|
111
|
+
update_reason_for_test(order, Concept.find(reason_for_test)&.id,
|
|
112
|
+
force_update: params.fetch('force_update', false))
|
|
106
113
|
end
|
|
107
114
|
|
|
108
115
|
Lab::LabOrderSerializer.serialize_order(order)
|
|
109
116
|
end
|
|
110
117
|
|
|
111
118
|
def void_order(order_id, reason)
|
|
112
|
-
order = Lab::LabOrder.includes(%i[requesting_clinician reason_for_test target_lab comment_to_fulfiller],
|
|
119
|
+
order = Lab::LabOrder.includes(%i[requesting_clinician reason_for_test target_lab comment_to_fulfiller],
|
|
120
|
+
tests: [:result])
|
|
113
121
|
.find(order_id)
|
|
114
122
|
|
|
115
123
|
order.requesting_clinician&.void(reason)
|
|
@@ -140,14 +148,55 @@ module Lab
|
|
|
140
148
|
value_text: order_params['status'],
|
|
141
149
|
creator: User.current.id
|
|
142
150
|
)
|
|
151
|
+
|
|
152
|
+
# Save order status trail if available
|
|
153
|
+
save_order_status_trail(order, order_params) if order_params['status']
|
|
143
154
|
end
|
|
144
155
|
create_rejection_notification(order_params) if order_params['status'] == 'test-rejected'
|
|
145
156
|
end
|
|
146
157
|
|
|
147
158
|
def update_order_result(order_params)
|
|
148
|
-
|
|
159
|
+
# Extract tracking number from nested structure if present
|
|
160
|
+
tracking_number = order_params['tracking_number'] || order_params.dig('order', 'tracking_number')
|
|
161
|
+
order = find_order(tracking_number)
|
|
162
|
+
|
|
149
163
|
order_dto = Lab::Lims::OrderSerializer.serialize_order(order)
|
|
150
|
-
|
|
164
|
+
|
|
165
|
+
# Handle results if present in the old format
|
|
166
|
+
patch_order_dto_with_lims_results!(order_dto, order_params['results']) if order_params['results']
|
|
167
|
+
|
|
168
|
+
# Handle test results in NLIMS format
|
|
169
|
+
if order_params['tests']
|
|
170
|
+
# Extract test results from NLIMS tests payload
|
|
171
|
+
test_results = {}
|
|
172
|
+
order_params['tests'].each do |test_data|
|
|
173
|
+
test_name = test_data.dig('test_type', 'name')
|
|
174
|
+
next unless test_name && test_data['test_results']
|
|
175
|
+
|
|
176
|
+
test_results[test_name] = {
|
|
177
|
+
'results' => test_data['test_results'].each_with_object({}) do |result, formatted|
|
|
178
|
+
measure_name = result.dig('measure', 'name')
|
|
179
|
+
result_value = result.dig('result', 'value')
|
|
180
|
+
next unless measure_name && result_value
|
|
181
|
+
|
|
182
|
+
formatted[measure_name] = { 'result_value' => result_value }
|
|
183
|
+
end,
|
|
184
|
+
'result_date' => test_data['test_results'].first&.dig('result', 'result_date'),
|
|
185
|
+
'result_entered_by' => {}
|
|
186
|
+
}
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
patch_order_dto_with_lims_results!(order_dto, test_results) unless test_results.empty?
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Save order status trail if available from NLIMS
|
|
193
|
+
if order_params['order'] && order_params['order']['status_trail']
|
|
194
|
+
save_order_status_trails_from_nlims(order, order_params['order']['status_trail'])
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Save test status trails if available from NLIMS
|
|
198
|
+
save_test_status_trails_from_nlims(order, order_params['tests']) if order_params['tests']
|
|
199
|
+
|
|
151
200
|
Lab::Lims::PullWorker.new(nil).process_order(order_dto)
|
|
152
201
|
end
|
|
153
202
|
|
|
@@ -160,13 +209,15 @@ module Lab
|
|
|
160
209
|
last_order_date: Lab::LabOrder.last&.start_date&.to_date,
|
|
161
210
|
lab_orders: []
|
|
162
211
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
212
|
+
if include_data
|
|
213
|
+
data[:lab_orders] = orders.map do |order|
|
|
214
|
+
Lab::LabOrderSerializer.serialize_order(
|
|
215
|
+
order, requesting_clinician: order.requesting_clinician,
|
|
216
|
+
reason_for_test: order.reason_for_test,
|
|
217
|
+
target_lab: order.target_lab
|
|
218
|
+
)
|
|
219
|
+
end
|
|
220
|
+
end
|
|
170
221
|
data
|
|
171
222
|
end
|
|
172
223
|
|
|
@@ -174,14 +225,14 @@ module Lab
|
|
|
174
225
|
|
|
175
226
|
def create_rejection_notification(order_params)
|
|
176
227
|
order = find_order order_params['tracking_number']
|
|
177
|
-
data = {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
228
|
+
data = { type: 'LIMS',
|
|
229
|
+
specimen: ConceptName.find_by(concept_id: order.concept_id)&.name,
|
|
230
|
+
accession_number: order&.accession_number,
|
|
231
|
+
order_date: order&.start_date,
|
|
232
|
+
arv_number: find_arv_number(order.patient_id),
|
|
233
|
+
patient_id: result.person_id,
|
|
234
|
+
ordered_by: order&.provider&.person&.name,
|
|
235
|
+
rejection_reason: order_params['comments'] }.as_json
|
|
185
236
|
NotificationService.new.create_notification('LIMS', data)
|
|
186
237
|
end
|
|
187
238
|
|
|
@@ -235,7 +286,9 @@ module Lab
|
|
|
235
286
|
encounter.encounter_datetime = order_params[:date] || Date.today
|
|
236
287
|
encounter.visit = Visit.find_by_uuid(visit) if Encounter.column_names.include?('visit_id')
|
|
237
288
|
encounter.provider_id = User.current&.person&.id if Encounter.column_names.include?('provider_id')
|
|
238
|
-
|
|
289
|
+
if Encounter.column_names.include?('program_id') && order_params[:program_id].present?
|
|
290
|
+
encounter.program_id = order_params[:program_id]
|
|
291
|
+
end
|
|
239
292
|
encounter.save!
|
|
240
293
|
encounter.reload
|
|
241
294
|
end
|
|
@@ -247,7 +300,6 @@ module Lab
|
|
|
247
300
|
|
|
248
301
|
concept = params.dig(:specimen, :concept)
|
|
249
302
|
concept ||= params.dig(:specimen, :concept_id)
|
|
250
|
-
concept ||= unknown_concept_id
|
|
251
303
|
|
|
252
304
|
order_type = nil
|
|
253
305
|
order_type = OrderType.find_by_order_type_id!(params[:order_type_id])&.id if params[:order_type_id].present?
|
|
@@ -260,7 +312,7 @@ module Lab
|
|
|
260
312
|
order.date_created = params[:date]&.to_date || Date.today if order.respond_to?(:date_created)
|
|
261
313
|
order.start_date = params[:date]&.to_date || Date.today if order.respond_to?(:start_date)
|
|
262
314
|
order.auto_expire_date = params[:end_date]
|
|
263
|
-
#
|
|
315
|
+
# NOTE: comment_to_fulfiller is a has_one association, not a field
|
|
264
316
|
# It will be created via add_comment_to_fulfiller method
|
|
265
317
|
order.accession_number = access_number
|
|
266
318
|
order.orderer = User.current&.user_id
|
|
@@ -349,7 +401,7 @@ module Lab
|
|
|
349
401
|
end
|
|
350
402
|
|
|
351
403
|
def unknown_concept_id
|
|
352
|
-
ConceptName.find_by_name!('Unknown').
|
|
404
|
+
ConceptName.find_by_name!('Unknown').concept
|
|
353
405
|
end
|
|
354
406
|
|
|
355
407
|
def update_reason_for_test(order, concept_id, force_update: false)
|
|
@@ -357,7 +409,10 @@ module Lab
|
|
|
357
409
|
|
|
358
410
|
return if order.reason_for_test&.value_coded == concept_id
|
|
359
411
|
|
|
360
|
-
|
|
412
|
+
if order.reason_for_test&.value_coded && !force_update
|
|
413
|
+
raise InvalidParameterError,
|
|
414
|
+
"Can't change reason for test once set"
|
|
415
|
+
end
|
|
361
416
|
|
|
362
417
|
order.reason_for_test&.delete
|
|
363
418
|
date = order.start_date if order.respond_to?(:start_date)
|
|
@@ -370,6 +425,150 @@ module Lab
|
|
|
370
425
|
obs.void('New Status Received from LIMS')
|
|
371
426
|
end
|
|
372
427
|
end
|
|
428
|
+
|
|
429
|
+
def create_initial_order_status_trail(order)
|
|
430
|
+
create_order_status_observation(
|
|
431
|
+
order: order,
|
|
432
|
+
status: 'Drawn',
|
|
433
|
+
timestamp: order.start_date || order.date_created || Time.now,
|
|
434
|
+
updated_by: {
|
|
435
|
+
'first_name' => User.current&.person&.names&.first&.given_name,
|
|
436
|
+
'last_name' => User.current&.person&.names&.first&.family_name,
|
|
437
|
+
'id' => User.current&.user_id&.to_s,
|
|
438
|
+
'phone_number' => nil
|
|
439
|
+
}
|
|
440
|
+
)
|
|
441
|
+
rescue StandardError => e
|
|
442
|
+
Rails.logger.warn("Failed to create initial order status trail: #{e.message}")
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def save_order_status_trail(order, status_params)
|
|
446
|
+
create_order_status_observation(
|
|
447
|
+
order: order,
|
|
448
|
+
status: status_params['status'],
|
|
449
|
+
timestamp: status_params['status_time'] || Time.now,
|
|
450
|
+
updated_by: status_params['updated_by'] || {}
|
|
451
|
+
)
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
def save_order_status_trails_from_nlims(order, status_trail)
|
|
455
|
+
return unless status_trail.is_a?(Array)
|
|
456
|
+
|
|
457
|
+
status_trail.each do |trail_entry|
|
|
458
|
+
create_order_status_observation(
|
|
459
|
+
order: order,
|
|
460
|
+
status: trail_entry['status'],
|
|
461
|
+
timestamp: trail_entry['timestamp'],
|
|
462
|
+
updated_by: trail_entry['updated_by'] || {}
|
|
463
|
+
)
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def save_test_status_trails_from_nlims(order, tests)
|
|
468
|
+
return unless tests.is_a?(Array)
|
|
469
|
+
|
|
470
|
+
tests.each do |test_data|
|
|
471
|
+
next unless test_data['status_trail'].is_a?(Array)
|
|
472
|
+
|
|
473
|
+
# Find the test by test_type
|
|
474
|
+
test_name = test_data.dig('test_type', 'name')
|
|
475
|
+
next unless test_name
|
|
476
|
+
|
|
477
|
+
# Find concept for this test
|
|
478
|
+
concept = Lab::Lims::Utils.find_concept_by_name(test_name)
|
|
479
|
+
next unless concept
|
|
480
|
+
|
|
481
|
+
# Find the test observation
|
|
482
|
+
test = order.tests.find_by(value_coded: concept.concept_id)
|
|
483
|
+
next unless test
|
|
484
|
+
|
|
485
|
+
# Save each status trail entry
|
|
486
|
+
test_data['status_trail'].each do |trail_entry|
|
|
487
|
+
create_test_status_observation(
|
|
488
|
+
test: test,
|
|
489
|
+
status: trail_entry['status'],
|
|
490
|
+
timestamp: trail_entry['timestamp'],
|
|
491
|
+
updated_by: trail_entry['updated_by'] || {}
|
|
492
|
+
)
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
# Creates an observation for order status trail using obs table
|
|
498
|
+
def create_order_status_observation(order:, status:, timestamp:, updated_by: {})
|
|
499
|
+
# Find concept
|
|
500
|
+
order_status_concept = ConceptName.find_by(name: 'Lab Order Status')&.concept
|
|
501
|
+
|
|
502
|
+
unless order_status_concept
|
|
503
|
+
Rails.logger.warn('Missing Lab Order Status concept')
|
|
504
|
+
return
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
# Check if this exact status already exists
|
|
508
|
+
return if Observation.unscoped.exists?(
|
|
509
|
+
person_id: order.patient_id,
|
|
510
|
+
order_id: order.order_id,
|
|
511
|
+
concept_id: order_status_concept.concept_id,
|
|
512
|
+
value_text: status,
|
|
513
|
+
obs_datetime: timestamp,
|
|
514
|
+
voided: 0
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
# Create status observation
|
|
518
|
+
Observation.create!(
|
|
519
|
+
person_id: order.patient_id,
|
|
520
|
+
encounter_id: order.encounter_id,
|
|
521
|
+
order_id: order.order_id,
|
|
522
|
+
concept_id: order_status_concept.concept_id,
|
|
523
|
+
value_text: status, # Store status as text
|
|
524
|
+
obs_datetime: timestamp,
|
|
525
|
+
comments: updated_by.to_json,
|
|
526
|
+
creator: User.current&.user_id || 1,
|
|
527
|
+
date_created: Time.now,
|
|
528
|
+
uuid: SecureRandom.uuid
|
|
529
|
+
)
|
|
530
|
+
rescue StandardError => e
|
|
531
|
+
Rails.logger.error("Failed to create order status observation: #{e.message}")
|
|
532
|
+
Rails.logger.error(e.backtrace.first(5).join("\n"))
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
# Creates an observation for test status trail using obs table
|
|
536
|
+
def create_test_status_observation(test:, status:, timestamp:, updated_by: {})
|
|
537
|
+
# Find concept
|
|
538
|
+
test_status_concept = ConceptName.find_by(name: 'Lab Test Status')&.concept
|
|
539
|
+
|
|
540
|
+
unless test_status_concept
|
|
541
|
+
Rails.logger.warn('Missing Lab Test Status concept')
|
|
542
|
+
return
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
# Check if this exact status already exists
|
|
546
|
+
return if Observation.unscoped.exists?(
|
|
547
|
+
person_id: test.person_id,
|
|
548
|
+
obs_group_id: test.obs_id,
|
|
549
|
+
concept_id: test_status_concept.concept_id,
|
|
550
|
+
value_text: status,
|
|
551
|
+
obs_datetime: timestamp,
|
|
552
|
+
voided: 0
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
# Create status observation
|
|
556
|
+
Observation.create!(
|
|
557
|
+
person_id: test.person_id,
|
|
558
|
+
encounter_id: test.encounter_id,
|
|
559
|
+
obs_group_id: test.obs_id, # Link to parent test observation
|
|
560
|
+
concept_id: test_status_concept.concept_id,
|
|
561
|
+
value_text: status, # Store status as text
|
|
562
|
+
obs_datetime: timestamp,
|
|
563
|
+
comments: updated_by.to_json,
|
|
564
|
+
creator: User.current&.user_id || 1,
|
|
565
|
+
date_created: Time.now,
|
|
566
|
+
uuid: SecureRandom.uuid
|
|
567
|
+
)
|
|
568
|
+
rescue StandardError => e
|
|
569
|
+
Rails.logger.error("Failed to create test status observation: #{e.message}")
|
|
570
|
+
Rails.logger.error(e.backtrace.first(5).join("\n"))
|
|
571
|
+
end
|
|
373
572
|
end
|
|
374
573
|
end
|
|
375
574
|
end
|
|
@@ -43,11 +43,7 @@ module Lab
|
|
|
43
43
|
ActiveRecord::Base.connection.commit_db_transaction
|
|
44
44
|
|
|
45
45
|
# Execute job synchronously
|
|
46
|
-
|
|
47
|
-
ProcessLabResultJob.perform_now(results_obs.id, serializer, result_enter_by)
|
|
48
|
-
rescue StandardError => e
|
|
49
|
-
Rails.logger.error("Lab::ResultsService: Error processing lab result job for test #{test_id}: #{e.message}")
|
|
50
|
-
end
|
|
46
|
+
ProcessLabResultJob.perform_now(results_obs.id, serializer, result_enter_by)
|
|
51
47
|
|
|
52
48
|
Rails.logger.info("Lab::ResultsService: Result created for test #{test_id} #{serializer}")
|
|
53
49
|
serializer
|
|
@@ -14,8 +14,7 @@ module Lab
|
|
|
14
14
|
|
|
15
15
|
tests = filter_tests(tests, test_type_id: filters.delete(:test_type_id),
|
|
16
16
|
patient_id: filters.delete(:patient_id),
|
|
17
|
-
patient: filters.delete(:patient)
|
|
18
|
-
)
|
|
17
|
+
patient: filters.delete(:patient))
|
|
19
18
|
|
|
20
19
|
tests = filter_tests_by_results(tests) if %w[1 true].include?(filters[:pending_results]&.downcase)
|
|
21
20
|
|
|
@@ -29,12 +28,11 @@ module Lab
|
|
|
29
28
|
def create_tests(order, date, tests_params)
|
|
30
29
|
raise InvalidParameterError, 'tests are required' if tests_params.nil? || tests_params.empty?
|
|
31
30
|
|
|
32
|
-
|
|
33
31
|
Lab::LabTest.transaction do
|
|
34
32
|
tests_params.map do |params|
|
|
35
33
|
concept_id = params[:concept_id]
|
|
36
34
|
concept_id = Concept.find_concept_by_uuid(params[:concept]).id if concept_id.nil?
|
|
37
|
-
|
|
35
|
+
|
|
38
36
|
test = Lab::LabTest.create!(
|
|
39
37
|
concept_id: ConceptName.find_by_name!(Lab::Metadata::TEST_TYPE_CONCEPT_NAME)
|
|
40
38
|
.concept_id,
|
|
@@ -45,6 +43,9 @@ module Lab
|
|
|
45
43
|
value_coded: concept_id
|
|
46
44
|
)
|
|
47
45
|
|
|
46
|
+
# Create initial test status trail
|
|
47
|
+
create_initial_test_status_trail(test, date)
|
|
48
|
+
|
|
48
49
|
Lab::TestSerializer.serialize(test, order:)
|
|
49
50
|
end
|
|
50
51
|
end
|
|
@@ -100,6 +101,49 @@ module Lab
|
|
|
100
101
|
value_coded: test_type_id
|
|
101
102
|
)
|
|
102
103
|
end
|
|
104
|
+
|
|
105
|
+
def create_initial_test_status_trail(test, date)
|
|
106
|
+
# Find concept
|
|
107
|
+
test_status_concept = ConceptName.find_by(name: 'Lab Test Status')&.concept
|
|
108
|
+
|
|
109
|
+
unless test_status_concept
|
|
110
|
+
Rails.logger.warn('Missing Lab Test Status concept')
|
|
111
|
+
return
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
timestamp = date || test.obs_datetime || Time.now
|
|
115
|
+
|
|
116
|
+
# Check if 'Drawn' status already exists for this test
|
|
117
|
+
return if Observation.unscoped.exists?(
|
|
118
|
+
person_id: test.person_id,
|
|
119
|
+
obs_group_id: test.obs_id,
|
|
120
|
+
concept_id: test_status_concept.concept_id,
|
|
121
|
+
value_text: 'Drawn',
|
|
122
|
+
voided: 0
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Create status observation with 'Drawn' as initial status
|
|
126
|
+
Observation.create!(
|
|
127
|
+
person_id: test.person_id,
|
|
128
|
+
encounter_id: test.encounter_id,
|
|
129
|
+
obs_group_id: test.obs_id, # Link to parent test observation
|
|
130
|
+
concept_id: test_status_concept.concept_id,
|
|
131
|
+
value_text: 'Drawn', # Store status as text
|
|
132
|
+
obs_datetime: timestamp,
|
|
133
|
+
status: 'FINAL',
|
|
134
|
+
comments: {
|
|
135
|
+
'first_name' => User.current&.person&.names&.first&.given_name,
|
|
136
|
+
'last_name' => User.current&.person&.names&.first&.family_name,
|
|
137
|
+
'id' => User.current&.user_id&.to_s,
|
|
138
|
+
'phone_number' => nil
|
|
139
|
+
}.to_json,
|
|
140
|
+
creator: User.current&.user_id || 1,
|
|
141
|
+
date_created: Time.now,
|
|
142
|
+
uuid: SecureRandom.uuid
|
|
143
|
+
)
|
|
144
|
+
rescue StandardError => e
|
|
145
|
+
Rails.logger.warn("Failed to create initial test status trail: #{e.message}")
|
|
146
|
+
end
|
|
103
147
|
end
|
|
104
148
|
end
|
|
105
149
|
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# This migration creates concepts for lab order and test status tracking using obs table
|
|
4
|
+
# Status values are stored as text (value_text), not coded values
|
|
5
|
+
class CreateLabStatusConcepts < ActiveRecord::Migration[5.2]
|
|
6
|
+
def up
|
|
7
|
+
ActiveRecord::Base.transaction do
|
|
8
|
+
# Find concept class and datatype
|
|
9
|
+
concept_class = ConceptClass.find_by(name: 'Finding') || ConceptClass.find_by(name: 'Misc')
|
|
10
|
+
text_datatype = ConceptDatatype.find_by(name: 'Text')
|
|
11
|
+
|
|
12
|
+
# Create Lab Order Status concept (stores status as value_text)
|
|
13
|
+
unless ConceptName.exists?(name: 'Lab Order Status')
|
|
14
|
+
order_status_concept = create_concept(
|
|
15
|
+
name: 'Lab Order Status',
|
|
16
|
+
class_id: concept_class.id,
|
|
17
|
+
datatype_id: text_datatype.id,
|
|
18
|
+
is_set: 0
|
|
19
|
+
)
|
|
20
|
+
puts "Created 'Lab Order Status' concept with ID: #{order_status_concept.concept_id}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Create Lab Test Status concept (stores status as value_text)
|
|
24
|
+
unless ConceptName.exists?(name: 'Lab Test Status')
|
|
25
|
+
test_status_concept = create_concept(
|
|
26
|
+
name: 'Lab Test Status',
|
|
27
|
+
class_id: concept_class.id,
|
|
28
|
+
datatype_id: text_datatype.id,
|
|
29
|
+
is_set: 0
|
|
30
|
+
)
|
|
31
|
+
puts "Created 'Lab Test Status' concept with ID: #{test_status_concept.concept_id}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
puts 'Lab status concepts created successfully'
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def down
|
|
39
|
+
ActiveRecord::Base.transaction do
|
|
40
|
+
# Remove concepts
|
|
41
|
+
concepts_to_remove = ['Lab Order Status', 'Lab Test Status']
|
|
42
|
+
|
|
43
|
+
concepts_to_remove.each do |name|
|
|
44
|
+
concept = ConceptName.find_by(name: name)&.concept
|
|
45
|
+
next unless concept
|
|
46
|
+
|
|
47
|
+
ConceptName.where(concept: concept).destroy_all
|
|
48
|
+
concept.destroy
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def create_concept(name:, class_id:, datatype_id:, is_set:)
|
|
56
|
+
concept = Concept.create!(
|
|
57
|
+
class_id: class_id,
|
|
58
|
+
datatype_id: datatype_id,
|
|
59
|
+
short_name: name,
|
|
60
|
+
retired: 0,
|
|
61
|
+
is_set: is_set,
|
|
62
|
+
creator: 1,
|
|
63
|
+
date_created: Time.current,
|
|
64
|
+
uuid: SecureRandom.uuid
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
ConceptName.create!(
|
|
68
|
+
concept: concept,
|
|
69
|
+
name: name,
|
|
70
|
+
locale: 'en',
|
|
71
|
+
locale_preferred: 1,
|
|
72
|
+
concept_name_type: 'FULLY_SPECIFIED',
|
|
73
|
+
creator: 1,
|
|
74
|
+
date_created: Time.current,
|
|
75
|
+
uuid: SecureRandom.uuid
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
concept
|
|
79
|
+
end
|
|
80
|
+
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: 2.1.
|
|
4
|
+
version: 2.1.9.pre.alpha
|
|
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-04
|
|
11
|
+
date: 2026-03-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: couchrest
|
|
@@ -302,6 +302,7 @@ files:
|
|
|
302
302
|
- db/migrate/20260119104240_add_fulfiller_fields_to_orders.rb
|
|
303
303
|
- db/migrate/20260119104241_create_comment_to_fulfiller_concept.rb
|
|
304
304
|
- db/migrate/20260128111557_add_program_id_to_encounter.rb
|
|
305
|
+
- db/migrate/20260226065149_create_lab_status_concepts.rb
|
|
305
306
|
- lib/auto12epl.rb
|
|
306
307
|
- lib/couch_bum/couch_bum.rb
|
|
307
308
|
- lib/generators/lab/install/USAGE
|
|
@@ -340,9 +341,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
340
341
|
version: '0'
|
|
341
342
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
342
343
|
requirements:
|
|
343
|
-
- - "
|
|
344
|
+
- - ">"
|
|
344
345
|
- !ruby/object:Gem::Version
|
|
345
|
-
version:
|
|
346
|
+
version: 1.3.1
|
|
346
347
|
requirements: []
|
|
347
348
|
rubygems_version: 3.4.1
|
|
348
349
|
signing_key:
|