his_emr_api_lab 1.1.21 → 1.1.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. metadata +3 -82
  3. data/MIT-LICENSE +0 -20
  4. data/README.md +0 -71
  5. data/Rakefile +0 -32
  6. data/app/controllers/lab/application_controller.rb +0 -6
  7. data/app/controllers/lab/labels_controller.rb +0 -17
  8. data/app/controllers/lab/orders_controller.rb +0 -38
  9. data/app/controllers/lab/reasons_for_test_controller.rb +0 -9
  10. data/app/controllers/lab/results_controller.rb +0 -19
  11. data/app/controllers/lab/specimen_types_controller.rb +0 -15
  12. data/app/controllers/lab/test_result_indicators_controller.rb +0 -9
  13. data/app/controllers/lab/test_types_controller.rb +0 -15
  14. data/app/controllers/lab/tests_controller.rb +0 -26
  15. data/app/jobs/lab/application_job.rb +0 -4
  16. data/app/jobs/lab/push_order_job.rb +0 -12
  17. data/app/jobs/lab/update_patient_orders_job.rb +0 -32
  18. data/app/jobs/lab/void_order_job.rb +0 -17
  19. data/app/mailers/lab/application_mailer.rb +0 -6
  20. data/app/models/lab/application_record.rb +0 -5
  21. data/app/models/lab/lab_accession_number_counter.rb +0 -13
  22. data/app/models/lab/lab_encounter.rb +0 -7
  23. data/app/models/lab/lab_order.rb +0 -58
  24. data/app/models/lab/lab_result.rb +0 -31
  25. data/app/models/lab/lab_test.rb +0 -19
  26. data/app/models/lab/lims_failed_import.rb +0 -4
  27. data/app/models/lab/lims_order_mapping.rb +0 -10
  28. data/app/serializers/lab/lab_order_serializer.rb +0 -55
  29. data/app/serializers/lab/result_serializer.rb +0 -36
  30. data/app/serializers/lab/test_serializer.rb +0 -29
  31. data/app/services/lab/accession_number_service.rb +0 -77
  32. data/app/services/lab/concepts_service.rb +0 -82
  33. data/app/services/lab/labelling_service/order_label.rb +0 -106
  34. data/app/services/lab/lims/api/blackhole_api.rb +0 -21
  35. data/app/services/lab/lims/api/couchdb_api.rb +0 -53
  36. data/app/services/lab/lims/api/mysql_api.rb +0 -316
  37. data/app/services/lab/lims/api/rest_api.rb +0 -416
  38. data/app/services/lab/lims/api/ws_api.rb +0 -121
  39. data/app/services/lab/lims/api_factory.rb +0 -19
  40. data/app/services/lab/lims/config.rb +0 -100
  41. data/app/services/lab/lims/exceptions.rb +0 -11
  42. data/app/services/lab/lims/migrator.rb +0 -216
  43. data/app/services/lab/lims/order_dto.rb +0 -105
  44. data/app/services/lab/lims/order_serializer.rb +0 -244
  45. data/app/services/lab/lims/pull_worker.rb +0 -289
  46. data/app/services/lab/lims/push_worker.rb +0 -149
  47. data/app/services/lab/lims/utils.rb +0 -91
  48. data/app/services/lab/lims/worker.rb +0 -86
  49. data/app/services/lab/metadata.rb +0 -24
  50. data/app/services/lab/orders_search_service.rb +0 -66
  51. data/app/services/lab/orders_service.rb +0 -212
  52. data/app/services/lab/results_service.rb +0 -147
  53. data/app/services/lab/tests_service.rb +0 -93
  54. data/config/routes.rb +0 -17
  55. data/db/migrate/20210126092910_create_lab_lab_accession_number_counters.rb +0 -12
  56. data/db/migrate/20210310115457_create_lab_lims_order_mappings.rb +0 -15
  57. data/db/migrate/20210323080140_change_lims_id_to_string_in_lims_order_mapping.rb +0 -15
  58. data/db/migrate/20210326195504_add_order_revision_to_lims_order_mapping.rb +0 -5
  59. data/db/migrate/20210407071728_create_lab_lims_failed_imports.rb +0 -19
  60. data/db/migrate/20210610095024_fix_numeric_results_value_type.rb +0 -20
  61. data/db/migrate/20210807111531_add_default_to_lims_order_mapping.rb +0 -7
  62. data/lib/auto12epl.rb +0 -201
  63. data/lib/couch_bum/couch_bum.rb +0 -92
  64. data/lib/generators/lab/install/USAGE +0 -9
  65. data/lib/generators/lab/install/install_generator.rb +0 -19
  66. data/lib/generators/lab/install/templates/rswag-ui-lab.rb +0 -5
  67. data/lib/generators/lab/install/templates/start_worker.rb +0 -32
  68. data/lib/generators/lab/install/templates/swagger.yaml +0 -714
  69. data/lib/his_emr_api_lab.rb +0 -5
  70. data/lib/lab/engine.rb +0 -15
  71. data/lib/lab/version.rb +0 -5
  72. data/lib/logger_multiplexor.rb +0 -38
  73. data/lib/tasks/lab_tasks.rake +0 -25
  74. data/lib/tasks/loaders/data/reasons-for-test.csv +0 -7
  75. data/lib/tasks/loaders/data/test-measures.csv +0 -225
  76. data/lib/tasks/loaders/data/tests.csv +0 -161
  77. data/lib/tasks/loaders/loader_mixin.rb +0 -53
  78. data/lib/tasks/loaders/metadata_loader.rb +0 -26
  79. data/lib/tasks/loaders/reasons_for_test_loader.rb +0 -23
  80. data/lib/tasks/loaders/specimens_loader.rb +0 -65
  81. data/lib/tasks/loaders/test_result_indicators_loader.rb +0 -54
@@ -1,289 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lab
4
- module Lims
5
- ##
6
- # Pulls orders from a Lims API object and saves them to the local database.
7
- class PullWorker
8
- attr_reader :lims_api
9
-
10
- include Utils # for logger
11
-
12
- LIMS_LOG_PATH = Rails.root.join('log', 'lims')
13
-
14
- def initialize(lims_api)
15
- @lims_api = lims_api
16
- end
17
-
18
- ##
19
- # Pulls orders from the LIMS queue and writes them to the local database
20
- def pull_orders(batch_size: 10_000, **kwargs)
21
- logger.info("Retrieving LIMS orders starting from #{last_seq}")
22
-
23
- lims_api.consume_orders(from: last_seq, limit: batch_size, **kwargs) do |order_dto, context|
24
- logger.debug("Retrieved order ##{order_dto[:tracking_number]}: #{order_dto}")
25
-
26
- patient = find_patient_by_nhid(order_dto[:patient][:id])
27
- unless patient
28
- logger.debug("Discarding order: Non local patient ##{order_dto[:patient][:id]} on order ##{order_dto[:tracking_number]}")
29
- order_rejected(order_dto, "Patient NPID, '#{order_dto[:patient][:id]}', didn't match any local NPIDs")
30
- next
31
- end
32
-
33
- if order_dto[:tests].empty?
34
- logger.debug("Discarding order: Missing tests on order ##{order_dto[:tracking_number]}")
35
- order_rejected(order_dto, 'Order is missing tests')
36
- next
37
- end
38
-
39
- diff = match_patient_demographics(patient, order_dto['patient'])
40
- if diff.empty?
41
- save_order(patient, order_dto)
42
- order_saved(order_dto)
43
- else
44
- save_failed_import(order_dto, 'Demographics not matching', diff)
45
- end
46
-
47
- update_last_seq(context.current_seq)
48
- rescue Lab::Lims::DuplicateNHID
49
- logger.warn("Failed to import order due to duplicate patient NHID: #{order_dto[:patient][:id]}")
50
- save_failed_import(order_dto, "Duplicate local patient NHID: #{order_dto[:patient][:id]}")
51
- rescue MissingAccessionNumber
52
- logger.warn("Failed to import order due to missing accession number: #{order_dto[:_id]}")
53
- save_failed_import(order_dto, 'Order missing tracking number')
54
- rescue LimsException => e
55
- logger.warn("Failed to import order due to #{e.class} - #{e.message}")
56
- save_failed_import(order_dto, e.message)
57
- end
58
- end
59
-
60
- protected
61
-
62
- def order_saved(order_dto); end
63
-
64
- def order_rejected(order_dto, message); end
65
-
66
- def last_seq
67
- File.open(last_seq_path, File::RDONLY | File::CREAT, 0o644) do |fin|
68
- data = fin.read&.strip
69
- return nil if data.blank?
70
-
71
- return data
72
- end
73
- end
74
-
75
- def update_last_seq(last_seq)
76
- File.open(last_seq_path, File::WRONLY | File::CREAT, 0o644) do |fout|
77
- fout.flock(File::LOCK_EX)
78
-
79
- fout.write(last_seq.to_s)
80
- end
81
- end
82
-
83
- private
84
-
85
- def find_patient_by_nhid(nhid)
86
- national_id_type = PatientIdentifierType.where(name: ['National id', 'Old Identification Number'])
87
- identifiers = PatientIdentifier.where(type: national_id_type, identifier: nhid)
88
- .joins('INNER JOIN person ON person.person_id = patient_identifier.patient_id AND person.voided = 0')
89
- if identifiers.count.zero?
90
- identifiers = PatientIdentifier.unscoped
91
- .where(voided: 1, type: national_id_type, identifier: nhid)
92
- .joins('INNER JOIN person ON person.person_id = patient_identifier.patient_id AND person.voided = 0')
93
- end
94
-
95
- # Joining to person above to ensure that the person is not voided,
96
- # it was noted at one site that there were some people that were voided
97
- # upon merging but the patient and patient_identifier was not voided
98
-
99
- return nil if identifiers.count.zero?
100
-
101
- patients = Patient.where(patient_id: identifiers.select(:patient_id))
102
- .distinct(:patient_id)
103
- .all
104
-
105
- raise Lab::Lims::DuplicateNHID, "Duplicate National Health ID: #{nhid}" if patients.size > 1
106
-
107
- patients.first
108
- end
109
-
110
- ##
111
- # Matches a local patient's demographics to a LIMS patient's demographics
112
- def match_patient_demographics(local_patient, lims_patient)
113
- diff = {}
114
- person = Person.find(local_patient.id)
115
- person_name = PersonName.find_by_person_id(local_patient.id)
116
-
117
- unless (person.gender.blank? && lims_patient['gender'].blank?)\
118
- || person.gender&.first&.casecmp?(lims_patient['gender']&.first)
119
- diff[:gender] = { local: person.gender, lims: lims_patient['gender'] }
120
- end
121
-
122
- unless names_match?(person_name&.given_name, lims_patient['first_name'])
123
- diff[:given_name] = { local: person_name&.given_name, lims: lims_patient['first_name'] }
124
- end
125
-
126
- unless names_match?(person_name&.family_name, lims_patient['last_name'])
127
- diff[:family_name] = { local: person_name&.family_name, lims: lims_patient['last_name'] }
128
- end
129
-
130
- diff
131
- end
132
-
133
- def names_match?(name1, name2)
134
- name1 = name1&.gsub(/'/, '')&.strip
135
- name2 = name2&.gsub(/'/, '')&.strip
136
-
137
- return true if name1.blank? && name2.blank?
138
-
139
- return false if name1.blank? || name2.blank?
140
-
141
- name1.casecmp?(name2)
142
- end
143
-
144
- def save_order(patient, order_dto)
145
- raise MissingAccessionNumber if order_dto[:tracking_number].blank?
146
-
147
- logger.info("Importing LIMS order ##{order_dto[:tracking_number]}")
148
- mapping = find_order_mapping_by_lims_id(order_dto[:_id])
149
-
150
- ActiveRecord::Base.transaction do
151
- if mapping
152
- order = update_order(patient, mapping.order_id, order_dto)
153
- mapping.update(pulled_at: Time.now)
154
- else
155
- order = create_order(patient, order_dto)
156
- mapping = LimsOrderMapping.create(lims_id: order_dto[:_id],
157
- order_id: order['id'],
158
- pulled_at: Time.now,
159
- revision: order_dto['_rev'])
160
- end
161
-
162
- order
163
- end
164
- end
165
-
166
- def create_order(patient, order_dto)
167
- logger.debug("Creating order ##{order_dto['_id']}")
168
- order = OrdersService.order_test(order_dto.to_order_service_params(patient_id: patient.patient_id))
169
- update_results(order, order_dto['test_results']) unless order_dto['test_results'].empty?
170
-
171
- order
172
- end
173
-
174
- def update_order(patient, order_id, order_dto)
175
- logger.debug("Updating order ##{order_dto['_id']}")
176
- order = OrdersService.update_order(order_id, order_dto.to_order_service_params(patient_id: patient.patient_id)
177
- .merge(force_update: 'true'))
178
- update_results(order, order_dto['test_results']) unless order_dto['test_results'].empty?
179
-
180
- order
181
- end
182
-
183
- def update_results(order, lims_results)
184
- logger.debug("Updating results for order ##{order[:accession_number]}: #{lims_results}")
185
-
186
- lims_results.each do |test_name, test_results|
187
- test = find_test(order['id'], test_name)
188
- unless test
189
- logger.warn("Couldn't find test, #{test_name}, in order ##{order[:id]}")
190
- next
191
- end
192
-
193
- next if test.result || test_results['results'].blank?
194
-
195
- measures = test_results['results'].map do |indicator, value|
196
- measure = find_measure(order, indicator, value)
197
- next nil unless measure
198
-
199
- measure
200
- end
201
-
202
- measures = measures.compact
203
- next if measures.empty?
204
-
205
- creator = format_result_entered_by(test_results['result_entered_by'])
206
-
207
- ResultsService.create_results(test.id, { provider_id: User.current.person_id,
208
- date: Utils.parse_date(test_results['date_result_entered'], order[:order_date].to_s),
209
- comments: "LIMS import: Entered by: #{creator}",
210
- measures: measures } )
211
- end
212
- end
213
-
214
- def find_test(order_id, test_name)
215
- test_name = Utils.translate_test_name(test_name)
216
- test_concept = Utils.find_concept_by_name(test_name)
217
- raise "Unknown test name, #{test_name}!" unless test_concept
218
-
219
- LabTest.find_by(order_id: order_id, value_coded: test_concept.concept_id)
220
- end
221
-
222
- def find_measure(_order, indicator_name, value)
223
- indicator = Utils.find_concept_by_name(indicator_name)
224
- unless indicator
225
- logger.warn("Result indicator #{indicator_name} not found in concepts list")
226
- return nil
227
- end
228
-
229
- value_modifier, value, value_type = parse_lims_result_value(value)
230
- return nil if value.blank?
231
-
232
- ActiveSupport::HashWithIndifferentAccess.new(
233
- indicator: { concept_id: indicator.concept_id },
234
- value_type: value_type,
235
- value: value_type == 'numeric' ? value.to_f : value,
236
- value_modifier: value_modifier.blank? ? '=' : value_modifier
237
- )
238
- end
239
-
240
- def parse_lims_result_value(value)
241
- value = value['result_value']&.strip
242
- return nil, nil, nil if value.blank?
243
-
244
- match = value&.match(/^(>|=|<|<=|>=)(.*)$/)
245
- return nil, value, guess_result_datatype(value) unless match
246
-
247
- [match[1], match[2].strip, guess_result_datatype(match[2])]
248
- end
249
-
250
- def guess_result_datatype(result)
251
- return 'numeric' if result.strip.match?(/^[+-]?((\d+(\.\d+)?)|\.\d+)$/)
252
-
253
- 'text'
254
- end
255
-
256
- def format_result_entered_by(result_entered_by)
257
- first_name = result_entered_by['first_name']
258
- last_name = result_entered_by['last_name']
259
- phone_number = result_entered_by['phone_number']
260
- id = result_entered_by['id'] # Looks like a user_id of some sort
261
-
262
- "#{id}:#{first_name} #{last_name}:#{phone_number}"
263
- end
264
-
265
- def save_failed_import(order_dto, reason, diff = nil)
266
- logger.info("Failed to import LIMS order ##{order_dto[:tracking_number]} due to '#{reason}'")
267
- LimsFailedImport.create!(lims_id: order_dto[:_id],
268
- tracking_number: order_dto[:tracking_number],
269
- patient_nhid: order_dto[:patient][:id],
270
- reason: reason,
271
- diff: diff&.to_json)
272
- end
273
-
274
- def last_seq_path
275
- LIMS_LOG_PATH.join('last_seq.dat')
276
- end
277
-
278
- def find_order_mapping_by_lims_id(lims_id)
279
- mapping = Lab::LimsOrderMapping.find_by(lims_id: lims_id)
280
- return nil unless mapping
281
-
282
- return mapping if Lab::LabOrder.where(order_id: mapping.order_id).exists?
283
-
284
- mapping.destroy
285
- nil
286
- end
287
- end
288
- end
289
- end
@@ -1,149 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lab
4
- module Lims
5
- ##
6
- # Pushes all local orders to a LIMS Api object.
7
- class PushWorker
8
- attr_reader :lims_api
9
-
10
- include Utils # for logger
11
-
12
- SECONDS_TO_WAIT_FOR_ORDERS = 30
13
-
14
- def initialize(lims_api)
15
- @lims_api = lims_api
16
- end
17
-
18
- def push_orders(batch_size: 1000, wait: false)
19
- loop do
20
- logger.info('Looking for new orders to push to LIMS...')
21
- orders = orders_pending_sync(batch_size).all
22
-
23
- logger.debug("Found #{orders.size} orders...")
24
- orders.each do |order|
25
- push_order(order)
26
- rescue GatewayError => e
27
- logger.error("Failed to push order ##{order.accession_number}: #{e.class} - #{e.message}")
28
- end
29
-
30
- break unless wait
31
-
32
- logger.info('Waiting for orders...')
33
- sleep(Lab::Lims::Config.updates_poll_frequency)
34
- end
35
- end
36
-
37
- def push_order_by_id(order_id)
38
- order = Lab::LabOrder.joins(order_type: { name: 'Lab' })
39
- .unscoped
40
- .find(order_id)
41
- push_order(order)
42
- end
43
-
44
- ##
45
- # Pushes given order to LIMS queue
46
- def push_order(order)
47
- logger.info("Pushing order ##{order.order_id}")
48
-
49
- order_dto = Lab::Lims::OrderSerializer.serialize_order(order)
50
- mapping = Lab::LimsOrderMapping.find_by(order_id: order.order_id)
51
-
52
- ActiveRecord::Base.transaction do
53
- if mapping && !order.voided.zero?
54
- Rails.logger.info("Deleting order ##{order_dto[:accession_number]} from LIMS")
55
- lims_api.delete_order(mapping.lims_id, order_dto)
56
- mapping.destroy
57
- elsif mapping
58
- Rails.logger.info("Updating order ##{order_dto[:accession_number]} in LIMS")
59
- lims_api.update_order(mapping.lims_id, order_dto)
60
- if order_dto['test_results'].nil? || order_dto['test_results'].empty?
61
- mapping.update(pushed_at: Time.now)
62
- else
63
- mapping.update(pushed_at: Time.now, result_push_status: true)
64
- end
65
- elsif order_dto[:_id] && Lab::LimsOrderMapping.where(lims_id: order_dto[:_id]).exists?
66
- # HACK: v1.1.7 had a bug where duplicates of recently created orders where being created by
67
- # the pull worker. This here detects those duplicates and voids them.
68
- Rails.logger.warn("Duplicate accession number found: #{order_dto[:_id]}, skipping order...")
69
- fix_duplicates!(order)
70
- else
71
- Rails.logger.info("Creating order ##{order_dto[:accession_number]} in LIMS")
72
- update = lims_api.create_order(order_dto)
73
- Lab::LimsOrderMapping.create!(order: order, lims_id: update['id'], revision: update['rev'],
74
- pushed_at: Time.now, result_push_status: false)
75
- end
76
- end
77
-
78
- order_dto
79
- end
80
-
81
- private
82
-
83
- def orders_pending_sync(batch_size)
84
- return new_orders.limit(batch_size) if new_orders.exists?
85
-
86
- return voided_orders.limit(batch_size) if voided_orders.exists?
87
-
88
- updated_orders.limit(batch_size)
89
- end
90
-
91
- def new_orders
92
- Rails.logger.debug('Looking for new orders that need to be created in LIMS...')
93
- Lab::LabOrder.where.not(order_id: Lab::LimsOrderMapping.all.select(:order_id))
94
- .order(date_created: :desc)
95
- end
96
-
97
- def updated_orders
98
- Rails.logger.debug('Looking for recently updated orders that need to be pushed to LIMS...')
99
- last_updated = Lab::LimsOrderMapping.select('MAX(updated_at) AS last_updated')
100
- .first
101
- .last_updated
102
-
103
- Lab::LabOrder.left_joins(:results)
104
- .joins(:mapping)
105
- .where('orders.discontinued_date > :last_updated
106
- OR obs.date_created > orders.date_created AND lab_lims_order_mappings.result_push_status = 0',
107
- last_updated: last_updated)
108
- .group('orders.order_id')
109
- .order(discontinued_date: :desc, date_created: :desc)
110
- end
111
-
112
- def voided_orders
113
- Rails.logger.debug('Looking for voided orders that are being tracked by LIMS...')
114
- Lab::LabOrder.unscoped
115
- .where(order_type: OrderType.where(name: Lab::Metadata::ORDER_TYPE_NAME),
116
- order_id: Lab::LimsOrderMapping.all.select(:order_id),
117
- voided: 1)
118
- .order(date_voided: :desc)
119
- end
120
-
121
- ##
122
- # HACK: Checks for duplicates previously created by version 1.1.7 pull worker bug due to this proving orders
123
- # that have not been pushed to LIMS as orders awaiting updates.
124
- def fix_duplicates!(order)
125
- return order.void('Duplicate created by bug in HIS-EMR-API-Lab v1.1.7') unless order_has_specimen?(order)
126
-
127
- duplicate_order = Lab::LabOrder.where(accession_number: order.accession_number)
128
- .where.not(order_id: order.order_id)
129
- .first
130
- return unless duplicate_order
131
-
132
- if !order_has_results?(order) && (order_has_results?(duplicate_order) || order_has_specimen?(duplicate_order))
133
- order.void('DUplicate created by bug in HIS-EMR-API-Lab v1.1.7')
134
- else
135
- duplicate_order.void('Duplicate created by bug in HIS-EMR-API-Lab v1.1.7')
136
- Lab::LimsOrderMapping.find_by_lims_id(order.accession_number)&.destroy
137
- end
138
- end
139
-
140
- def order_has_results?(order)
141
- order.results.exists?
142
- end
143
-
144
- def order_has_specimen?(order)
145
- order.concept_id == ConceptName.find_by_name!('Unknown').concept_id
146
- end
147
- end
148
- end
149
- end
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'cgi/util'
4
-
5
- module Lab
6
- module Lims
7
- ##
8
- # Various helper methods for modules in the Lims namespaces...
9
- module Utils
10
- LIMS_LOG_PATH = Rails.root.join('log', 'lims')
11
- FileUtils.mkdir_p(LIMS_LOG_PATH) unless File.exist?(LIMS_LOG_PATH)
12
-
13
- def logger
14
- Rails.logger
15
- end
16
-
17
- TEST_NAME_MAPPINGS = {
18
- # For some weird reason(s) some tests have multiple names in LIMS,
19
- # this is used to sanitize those names.
20
- 'hiv_viral_load' => 'HIV Viral Load',
21
- 'viral laod' => 'HIV Viral Load',
22
- 'viral load' => 'HIV Viral Load',
23
- 'i/ink' => 'India ink',
24
- 'indian ink' => 'India ink'
25
- }.freeze
26
-
27
- def self.translate_test_name(test_name)
28
- TEST_NAME_MAPPINGS.fetch(test_name.downcase, test_name)
29
- end
30
-
31
- def self.structify(object)
32
- if object.is_a?(Hash)
33
- object.each_with_object(OpenStruct.new) do |kv_pair, struct|
34
- key, value = kv_pair
35
-
36
- struct[key] = structify(value)
37
- end
38
- elsif object.respond_to?(:map)
39
- object.map { |item| structify(item) }
40
- else
41
- object
42
- end
43
- end
44
-
45
- def self.lab_user
46
- user = User.find_by_username('lab_daemon')
47
- return user if user
48
-
49
- god_user = User.first
50
-
51
- person = Person.create!(creator: god_user.user_id)
52
- PersonName.create!(person: person, given_name: 'Lab', family_name: 'Daemon', creator: god_user.user_id)
53
-
54
- User.create!(username: 'lab_daemon', person: person, creator: god_user.user_id)
55
- end
56
-
57
- def self.parse_date(str_date, fallback_date = nil)
58
- str_date = str_date&.to_s
59
-
60
- if str_date.blank? && fallback_date.blank?
61
- raise "Can't parse blank date"
62
- end
63
-
64
- return parse_date(fallback_date) if str_date.blank?
65
-
66
- str_date = str_date.gsub(/^00/, '20').gsub(/^180/, '20')
67
-
68
- case str_date
69
- when /\d{4}-\d{2}-\d{2}/
70
- str_date
71
- when /\d{2}-\d{2}-\d{2}/
72
- Date.strptime(str_date, '%d-%m-%Y').strftime('%Y-%m-%d')
73
- when /(\d{4}\d{2}\d{2})\d+/
74
- Date.strptime(str_date, '%Y%m%d').strftime('%Y-%m-%d')
75
- when %r{\d{2}/\d{2}/\d{4}}
76
- str_date.to_date.to_s
77
- else
78
- Rails.logger.warn("Invalid date: #{str_date}")
79
- parse_date(fallback_date)
80
- end
81
- end
82
-
83
- def self.find_concept_by_name(name)
84
- ConceptName.joins(:concept)
85
- .merge(Concept.all) # Filter out voided
86
- .where(name: CGI.unescapeHTML(name))
87
- .first
88
- end
89
- end
90
- end
91
- end
@@ -1,86 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'logger_multiplexor'
4
-
5
- require_relative './api/couchdb_api'
6
-
7
- module Lab
8
- module Lims
9
- ##
10
- # Pull/Push orders from/to the LIMS queue (Oops meant CouchDB).
11
- module Worker
12
- def self.start
13
- User.current = Utils.lab_user
14
-
15
- fork(&method(:start_push_worker))
16
- fork(&method(:start_pull_worker))
17
- fork(&method(:start_realtime_pull_worker)) if realtime_updates_enabled?
18
-
19
- Process.waitall
20
- end
21
-
22
- def self.start_push_worker
23
- start_worker('push_worker') do
24
- worker = PushWorker.new(lims_api)
25
-
26
- worker.push_orders # (wait: true)
27
- end
28
- end
29
-
30
- def self.start_pull_worker
31
- start_worker('pull_worker') do
32
- worker = PullWorker.new(lims_api)
33
-
34
- worker.pull_orders
35
- end
36
- end
37
-
38
- def self.start_realtime_pull_worker
39
- start_worker('realtime_pull_worker') do
40
- worker = PullWorker.new(Lims::Api::WsApi.new(Lab::Lims::Config.updates_socket))
41
-
42
- worker.pull_orders
43
- end
44
- end
45
-
46
- LOG_FILES_TO_KEEP = 5
47
- LOG_FILE_SIZE = 500.megabytes
48
-
49
- def self.start_worker(worker_name)
50
- Rails.logger = LoggerMultiplexor.new(file_logger(worker_name), $stdout)
51
- ActiveRecord::Base.logger = Rails.logger
52
- Rails.logger.level = :debug
53
-
54
- File.open(log_path("#{worker_name}.lock"), File::RDWR | File::CREAT, 0o644) do |fout|
55
- unless fout.flock(File::LOCK_EX | File::LOCK_NB)
56
- Rails.logger.warn("Another process already holds lock #{worker_name} (#{fout.read}), exiting...")
57
- break
58
- end
59
-
60
- fout.write("Locked by process ##{Process.pid} under process group ##{Process.ppid} at #{Time.now}")
61
- fout.flush
62
- yield
63
- end
64
- end
65
-
66
- def self.file_logger(worker_name)
67
- Logger.new(log_path("#{worker_name}.log"), LOG_FILES_TO_KEEP, LOG_FILE_SIZE)
68
- end
69
-
70
- def self.log_path(filename)
71
- Lab::Lims::Utils::LIMS_LOG_PATH.join(filename)
72
- end
73
-
74
- def self.realtime_updates_enabled?
75
- Lims::Config.updates_socket.key?('url')
76
- rescue Lab::Lims::Config::ConfigNotFound => e
77
- Rails.logger.warn("Check for realtime updates failed: #{e.message}")
78
- false
79
- end
80
-
81
- def self.lims_api
82
- Lab::Lims::ApiFactory.create_api
83
- end
84
- end
85
- end
86
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lab
4
- module Metadata
5
- # Concepts
6
- REASON_FOR_TEST_CONCEPT_NAME = 'Reason for test'
7
- REQUESTING_CLINICIAN_CONCEPT_NAME = 'Person making request'
8
- SPECIMEN_TYPE_CONCEPT_NAME = 'Specimen type'
9
- TARGET_LAB_CONCEPT_NAME = 'Lab'
10
- TEST_RESULT_CONCEPT_NAME = 'Lab test result'
11
- TEST_RESULT_INDICATOR_CONCEPT_NAME = 'Lab test result indicator'
12
- TEST_TYPE_CONCEPT_NAME = 'Test type'
13
- UNKNOWN_SPECIMEN = 'Unknown'
14
-
15
- # Encounter
16
- ENCOUNTER_TYPE_NAME = 'Lab'
17
-
18
- # Order types
19
- ORDER_TYPE_NAME = 'Lab'
20
-
21
- # Programs
22
- LAB_PROGRAM_NAME = 'Laboratory Program'
23
- end
24
- end