dde_client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,643 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DdeClient::DdeService
4
+ require_relative './matcher'
5
+
6
+ class DdeError < StandardError; end
7
+
8
+ Dde_CONFIG_PATH = Rails.root.join('config', 'dde.yml')
9
+ LOGGER = Rails.logger
10
+
11
+ # Limit all find queries for local patients to this
12
+ PATIENT_SEARCH_RESULTS_LIMIT = 10
13
+
14
+ attr_accessor :visit_type
15
+
16
+ include ModelUtils
17
+
18
+ def initialize(visit_type:)
19
+ raise InvalidParameterError, 'VisitType (visit_type_id) is required' unless visit_type
20
+
21
+ @visit_type = visit_type
22
+ end
23
+
24
+ def self.dde_enabled?
25
+ property = GlobalProperty.find_by_property('dde_enabled')&.property_value
26
+ return false unless property
27
+
28
+ case property
29
+ when /true/i then true
30
+ when /false/i then false
31
+ else raise "Invalid value for property dde_enabled: #{property.property_value}"
32
+ end
33
+ end
34
+
35
+ def test_connection
36
+ response = { connection_available: false, message: 'No connection to Dde', status: 500 }
37
+ begin
38
+ result, status = dde_client
39
+ response[:connection_available] = status == 200
40
+ response[:message] = result
41
+ rescue StandardError => exception
42
+ LOGGER.error "Failed to connect to Dde: #{exception.message}"
43
+ response[:message] = exception.message
44
+ end
45
+ response
46
+ end
47
+
48
+ # Registers local OpenMRS patient in Dde
49
+ #
50
+ # On success patient get two identifiers under the types
51
+ # 'Dde person document ID' and 'National id'. The
52
+ # 'Dde person document ID' is the patient's record ID in the local
53
+ # Dde instance and the 'National ID' is the national unique identifier
54
+ # for the patient.
55
+ def create_patient(patient)
56
+ push_local_patient_to_dde(patient)
57
+ end
58
+
59
+ def remaining_npids
60
+ response, status = dde_client.get("/location_npid_status?location_id=#{Location.current_location.id}")
61
+ raise DdeError, "Failed to fetch remaining npids: #{status} - #{response}" unless status == 200
62
+
63
+ response
64
+ end
65
+
66
+ def void_patient(patient, reason)
67
+ raise ArgumentError, "Can't request a Dde void for a non-voided patient" unless patient.voided?
68
+ raise ArgumentError, 'void_reason is required' if reason.blank?
69
+
70
+ doc_id = PatientIdentifier.unscoped
71
+ .where(identifier_type: dde_doc_id_type, patient: patient)
72
+ .order(:date_voided)
73
+ .last
74
+ &.identifier
75
+ return patient unless doc_id
76
+
77
+ response, status = dde_client.delete("void_person/#{doc_id}?void_reason=#{reason}")
78
+ raise DdeError, "Failed to void person in Dde: #{status} - #{response}" unless status == 200
79
+
80
+ patient
81
+ end
82
+
83
+ # Updates patient demographics in Dde.
84
+ #
85
+ # Local patient is not affected in anyway by the update
86
+ def update_patient(patient)
87
+ dde_patient = openmrs_to_dde_patient(patient)
88
+ response, status = dde_client.post('update_person', dde_patient)
89
+
90
+ raise DdeError, "Failed to update person in Dde: #{response}" unless status == 200
91
+
92
+ patient
93
+ end
94
+
95
+ ##
96
+ # Pushes a footprint for patient in current visit_type to Dde
97
+ def create_patient_footprint(patient, date = nil, creator_id = nil)
98
+ LOGGER.debug("Pushing footprint to Dde for patient ##{patient.patient_id}")
99
+ doc_id = find_patient_doc_id(patient)
100
+ unless doc_id
101
+ LOGGER.debug("Patient ##{patient.patient_id} is not a Dde patient")
102
+ return
103
+ end
104
+
105
+ response, status = dde_client.post('update_footprint', person_uuid: doc_id,
106
+ location_id: Location.current_location_health_center.location_id,
107
+ visit_type_id: visit_type.id,
108
+ encounter_datetime: date || Date.tody,
109
+ user_id: creator_id || User.current.user_id)
110
+
111
+ LOGGER.warn("Failed to push patient footprint to Dde: #{status} - #{response}") unless status == 200
112
+ end
113
+
114
+ ##
115
+ # Updates local patient with demographics currently in Dde.
116
+ def update_local_patient(patient, update_npid: false)
117
+ doc_id = patient_doc_id(patient)
118
+ unless doc_id
119
+ Rails.logger.warn("No Dde doc_id found for patient ##{patient.patient_id}")
120
+ push_local_patient_to_dde(patient)
121
+ return patient
122
+ end
123
+
124
+ dde_patient = find_remote_patients_by_doc_id(doc_id).first
125
+ unless dde_patient
126
+ Rails.logger.warn("Couldn't find patient ##{patient.patient_id} in Dde by doc_id ##{doc_id}")
127
+ push_local_patient_to_dde(patient)
128
+ return patient
129
+ end
130
+
131
+ if update_npid
132
+ merging_service.link_local_to_remote_patient(patient, dde_patient)
133
+ return patient
134
+ end
135
+
136
+ person_service.update_person(patient.person, dde_patient_to_local_person(dde_patient))
137
+ patient
138
+ end
139
+
140
+ # Import patients from Dde using doc id
141
+ def import_patients_by_doc_id(doc_id)
142
+ doc_id_type = patient_identifier_type('Dde person document id')
143
+ locals = patient_service.find_patients_by_identifier(doc_id, doc_id_type).limit(PATIENT_SEARCH_RESULTS_LIMIT)
144
+ remotes = find_remote_patients_by_doc_id(doc_id)
145
+
146
+ import_remote_patient(locals, remotes)
147
+ end
148
+
149
+ # Imports patients from Dde to the local database
150
+ def import_patients_by_npid(npid)
151
+ doc_id_type = patient_identifier_type('National id')
152
+ locals = patient_service.find_patients_by_identifier(npid, doc_id_type).limit(PATIENT_SEARCH_RESULTS_LIMIT)
153
+ remotes = find_remote_patients_by_npid(npid)
154
+
155
+ import_remote_patient(locals, remotes)
156
+ end
157
+
158
+ # Similar to import_patients_by_npid but uses name and gender instead of npid
159
+ def import_patients_by_name_and_gender(given_name, family_name, gender)
160
+ locals = patient_service.find_patients_by_name_and_gender(given_name, family_name, gender).limit(PATIENT_SEARCH_RESULTS_LIMIT)
161
+
162
+ remotes = begin
163
+ find_remote_patients_by_name_and_gender(given_name, family_name, gender)
164
+ rescue StandardError
165
+ []
166
+ end
167
+
168
+ import_remote_patient(locals, remotes)
169
+ end
170
+
171
+ def find_patients_by_npid(npid)
172
+ locals = patient_service.find_patients_by_npid(npid).limit(PATIENT_SEARCH_RESULTS_LIMIT)
173
+ remotes = find_remote_patients_by_npid(npid)
174
+
175
+ package_patients(locals, remotes, auto_push_singular_local: true)
176
+ end
177
+
178
+ def find_patients_by_name_and_gender(given_name, family_name, gender)
179
+ locals = []
180
+ begin
181
+ locals = patient_service.find_patients_by_name_and_gender(given_name, family_name, gender).limit(PATIENT_SEARCH_RESULTS_LIMIT)
182
+ remotes = find_remote_patients_by_name_and_gender(given_name, family_name, gender)
183
+
184
+ package_patients(locals, remotes)
185
+ rescue StandardError => e
186
+ Rails.logger.warn("Error packaging patients: #{e.message}")
187
+ package_patients(locals, [])
188
+ end
189
+ end
190
+
191
+ def find_patient_updates(local_patient_id)
192
+ dde_doc_id_type = PatientIdentifierType.where(name: 'Dde Person Document ID')
193
+ doc_id = PatientIdentifier.find_by(patient_id: local_patient_id, identifier_type: dde_doc_id_type)
194
+ &.identifier
195
+ return nil unless doc_id
196
+
197
+ remote_patient = find_remote_patients_by_doc_id(doc_id).first
198
+ return nil unless remote_patient
199
+
200
+ Matcher.find_differences(Person.find(local_patient_id), remote_patient)
201
+ rescue DdeError => e
202
+ Rails.logger.warn("Check for Dde patient updates failed: #{e.message}")
203
+ nil
204
+ end
205
+
206
+ # Matches patients using a bunch of demographics
207
+ def match_patients_by_demographics(family_name:, given_name:, birthdate:,
208
+ gender:, home_district:, home_traditional_authority:,
209
+ home_village:, birthdate_estimated: 0)
210
+ response, status = dde_client.post(
211
+ 'search/people', family_name: family_name,
212
+ given_name: given_name,
213
+ gender: gender,
214
+ birthdate: birthdate,
215
+ birthdate_estimated: !birthdate_estimated.zero?,
216
+ attributes: {
217
+ home_district: home_district,
218
+ home_traditional_authority: home_traditional_authority,
219
+ home_village: home_village
220
+ }
221
+ )
222
+
223
+ raise DdeError, "Dde patient search failed: #{status} - #{response}" unless status == 200
224
+
225
+ response.collect do |match|
226
+ doc_id = match['person']['id']
227
+ patient = patient_service.find_patients_by_identifier(
228
+ doc_id, patient_identifier_type('Dde person document id')
229
+ ).first
230
+ match['person']['patient_id'] = patient&.id
231
+ match
232
+ end
233
+ end
234
+
235
+ # Trigger a merge of patients in Dde
236
+ def merge_patients(primary_patients_ids, secondary_patient_ids)
237
+ merging_service.merge_patients(primary_patients_ids, secondary_patient_ids)
238
+ end
239
+
240
+ def reassign_patient_npid(patient_ids)
241
+ patient_id = patient_ids['patient_id']
242
+ doc_id = patient_ids['doc_id']
243
+
244
+ raise InvalidParameterError, 'patient_id and/or doc_id required' if patient_id.blank? && doc_id.blank?
245
+
246
+ if doc_id.blank?
247
+ # Only have patient id thus we have patient locally only
248
+ return push_local_patient_to_dde(Patient.find(patient_ids['patient_id']))
249
+ end
250
+
251
+ # NOTE: Fail patient retrieval as early as possible before making any
252
+ # changes to Dde (ie if patient_id does not exist)
253
+ patient = patient_id.blank? ? nil : Patient.find(patient_id)
254
+
255
+ # We have a doc_id thus we can re-assign npid in Dde
256
+ # Check if person if available in Dde if not add person using doc_id
257
+ response, status = dde_client.post('search_by_doc_id', doc_id: doc_id)
258
+ if !response.blank? && status.to_i == 200
259
+ response, status = dde_client.post('reassign_npid', doc_id: doc_id)
260
+ elsif response.blank? && status.to_i == 200
261
+ return push_local_patient_to_dde(Patient.find(patient_ids['patient_id']))
262
+ end
263
+
264
+ unless status == 200 && !response.empty?
265
+ # The Dde's reassign_npid end point responds with a 200 - OK but returns
266
+ # an empty object when patient with given doc_id is not found.
267
+ raise DdeError, "Failed to reassign npid: Dde Response => #{status} - #{response}"
268
+ end
269
+
270
+ return save_remote_patient(response) unless patient
271
+
272
+ merging_service.link_local_to_remote_patient(patient, response)
273
+ end
274
+
275
+ # Convert a Dde person to an openmrs person.
276
+ #
277
+ # NOTE: This creates a person on the database.
278
+ def save_remote_patient(remote_patient)
279
+ LOGGER.debug "Converting Dde person to openmrs: #{remote_patient}"
280
+ params = dde_patient_to_local_person(remote_patient)
281
+
282
+ Person.transaction do
283
+ person = person_service.create_person(params)
284
+
285
+ patient = Patient.create(patient_id: person.id)
286
+ merging_service.link_local_to_remote_patient(patient, remote_patient)
287
+ end
288
+ end
289
+
290
+ ##
291
+ # Converts a dde_patient object into an object that can be passed to the person_service
292
+ # to create or update a person.
293
+ def dde_patient_to_local_person(dde_patient)
294
+ attributes = dde_patient.fetch('attributes')
295
+
296
+ # ActiveSupport::HashWithIndifferentAccess.new(
297
+ # birthdate: dde_patient.fetch('birthdate'),
298
+ # birthdate_estimated: dde_patient.fetch('birthdate_estimated'),
299
+ # gender: dde_patient.fetch('gender'),
300
+ # given_name: dde_patient.fetch('given_name'),
301
+ # family_name: dde_patient.fetch('family_name'),
302
+ # middle_name: dde_patient.fetch('middle_name'),
303
+ # home_village: attributes.fetch('home_village'),
304
+ # home_traditional_authority: attributes.fetch('home_traditional_authority'),
305
+ # home_district: attributes.fetch('home_district'),
306
+ # current_village: attributes.fetch('current_village'),
307
+ # current_traditional_authority: attributes.fetch('current_traditional_authority'),
308
+ # current_district: attributes.fetch('current_district')
309
+ # # cell_phone_number: attributes.fetch('cellphone_number'),
310
+ # # occupation: attributes.fetch('occupation')
311
+ # )
312
+ {
313
+ birthdate: dde_patient.fetch('birthdate'),
314
+ birthdate_estimated: dde_patient.fetch('birthdate_estimated'),
315
+ gender: dde_patient.fetch('gender'),
316
+ names: [
317
+ {
318
+ given_name: dde_patient.fetch('given_name'),
319
+ family_name: dde_patient.fetch('family_name'),
320
+ middle_name: dde_patient.fetch('middle_name')
321
+ }
322
+ ],
323
+ addresses: [
324
+ {
325
+ address1: attributes.fetch('home_district'),
326
+ address3: attributes.fetch('current_district'),
327
+ county_district: attributes.fetch('home_traditional_authority'),
328
+ state_province: attributes.fetch('current_traditional_authority'),
329
+ address2: attributes.fetch('home_village'),
330
+ city_village: attributes.fetch('current_village')
331
+ }
332
+ ]
333
+ }
334
+ end
335
+
336
+ private
337
+
338
+ def find_remote_patients_by_npid(npid)
339
+ response, _status = dde_client.post('search_by_npid', npid: npid)
340
+ raise DdeError, "Patient search by npid failed: Dde Response => #{response}" unless response.instance_of?(Array)
341
+
342
+ response
343
+ end
344
+
345
+ def find_remote_patients_by_name_and_gender(given_name, family_name, gender)
346
+ response = nil
347
+ begin
348
+ response, _status = dde_client.post('search_by_name_and_gender', given_name: given_name,
349
+ family_name: family_name,
350
+ gender: gender)
351
+ unless response.instance_of?(Array)
352
+ print "Patient search by name and gender failed: Dde Response => #{response}"
353
+ return []
354
+ end
355
+ rescue StandardError => e
356
+ print "Patient search by name and gender failed: Dde Response => #{e}"
357
+ return []
358
+ end
359
+
360
+ response
361
+ end
362
+
363
+ def find_remote_patients_by_doc_id(doc_id)
364
+ Rails.logger.info("Searching for Dde patient by doc_id ##{doc_id}")
365
+ response, _status = dde_client.post('search_by_doc_id', doc_id: doc_id)
366
+ raise DdeError, "Patient search by doc_id failed: Dde Response => #{response}" unless response.instance_of?(Array)
367
+
368
+ response
369
+ end
370
+
371
+ def find_patient_doc_id(patient)
372
+ patient.patient_identifiers.where(identifier_type: dde_doc_id_type).first
373
+ end
374
+
375
+ # Resolves local and remote patients and post processes the remote
376
+ # patients to take on a structure similar to that of local
377
+ # patients.
378
+ def package_patients(local_patients, remote_patients, auto_push_singular_local: false)
379
+ patients = resolve_patients(local_patients: local_patients,
380
+ remote_patients: remote_patients,
381
+ auto_push_singular_local: auto_push_singular_local)
382
+
383
+ # In some cases we may have remote patients that were previously imported but
384
+ # whose NPID has changed, we need to find and resolve these local patients.
385
+ unresolved_patients = find_patients_by_doc_id(patients[:remotes].collect { |remote_patient| remote_patient['doc_id'] })
386
+ if unresolved_patients.empty?
387
+ return { locals: patients[:locals], remotes: patients[:remotes].collect { |patient| localise_remote_patient(patient) } }
388
+ end
389
+
390
+ additional_patients = resolve_patients(local_patients: unresolved_patients, remote_patients: patients[:remotes])
391
+
392
+ {
393
+ locals: patients[:locals] + additional_patients[:locals],
394
+ remotes: additional_patients[:remotes].collect { |patient| localise_remote_patient(patient) }
395
+ }
396
+ end
397
+
398
+ # Locally saves the first unresolved remote patient.
399
+ #
400
+ # Method internally calls resolve_patients on the passed arguments then
401
+ # attempts to save the first unresolved patient in the local database.
402
+ #
403
+ # Returns: The imported patient (or nil if no local and remote patients are
404
+ # present).
405
+ def import_remote_patient(local_patients, remote_patients)
406
+ patients = resolve_patients(local_patients: local_patients, remote_patients: remote_patients)
407
+
408
+ return patients[:locals].first if patients[:remotes].empty?
409
+
410
+ save_remote_patient(patients[:remotes].first)
411
+ end
412
+
413
+ # Filters out @{param remote_patients} that exist in @{param local_patients}.
414
+ #
415
+ # Returns a hash with all resolved and unresolved remote patients:
416
+ #
417
+ # { resolved: [..,], locals: [...], remotes: [...] }
418
+ #
419
+ # NOTE: All resolved patients are available in the local database
420
+ def resolve_patients(local_patients:, remote_patients:, auto_push_singular_local: false)
421
+ remote_patients = remote_patients.dup # Will be modifying this copy
422
+
423
+ # Match all locals to remotes, popping out the matching patients from
424
+ # the list of remotes. The remaining remotes are considered unresolved
425
+ # remotes.
426
+ resolved_patients = local_patients.each_with_object([]) do |local_patient, resolved_patients|
427
+ # Local patient present on remote?
428
+ remote_patient = remote_patients.detect do |patient|
429
+ same_patient?(local_patient: local_patient, remote_patient: patient)
430
+ end
431
+
432
+ remote_patients.delete(remote_patient) if remote_patient
433
+
434
+ resolved_patients << local_patient
435
+ end
436
+
437
+ if resolved_patients.empty? && (local_patients.size.zero? && remote_patients.size == 1)
438
+ # HACK: Frontenders requested that if only a single patient exists
439
+ # remotely and locally none exists, the remote patient should be
440
+ # imported.
441
+ local_patient = find_patients_by_doc_id(remote_patients[0]['doc_id']).first
442
+ resolved_patients = [local_patient || save_remote_patient(remote_patients[0])]
443
+ remote_patients = []
444
+ elsif auto_push_singular_local && resolved_patients.size == 1\
445
+ && remote_patients.empty? && local_only_patient?(resolved_patients.first)
446
+ # ANOTHER HACK: Push local only patient to Dde
447
+ resolved_patients = [push_local_patient_to_dde(resolved_patients[0])]
448
+ else
449
+ resolved_patients = local_patients
450
+ end
451
+
452
+ { locals: resolved_patients, remotes: remote_patients }
453
+ end
454
+
455
+ # Checks if patient only exists on local database
456
+ def local_only_patient?(patient)
457
+ !(patient.patient_identifiers.where(identifier_type: patient_identifier_type('National id')).exists?\
458
+ && patient.patient_identifiers.where(identifier_type: patient_identifier_type('Dde person document id')).exists?)
459
+ end
460
+
461
+ # Matches local and remote patient
462
+ def same_patient?(local_patient:, remote_patient:)
463
+ PatientIdentifier.where(patient: local_patient, identifier_type: dde_doc_id_type).any? do |doc_id|
464
+ doc_id.identifier == remote_patient['doc_id']
465
+ end
466
+ end
467
+
468
+ # Saves local patient to Dde and links the two using the IDs
469
+ # generated by Dde.
470
+ def push_local_patient_to_dde(patient)
471
+ Rails.logger.info("Pushing local patient ##{patient.patient_id} to Dde")
472
+ response, status = dde_client.post('add_person', openmrs_to_dde_patient(patient))
473
+
474
+ if status == 422
475
+ error = UnprocessableEntityError.new("Failed to create patient in Dde: #{response.to_json}")
476
+ error.add_entity(patient)
477
+ raise error
478
+ end
479
+
480
+ raise response.to_json if status != 200
481
+
482
+ merging_service.link_local_to_remote_patient(patient, response)
483
+ end
484
+
485
+ # Converts a remote patient coming from Dde into a structure similar
486
+ # to that of a local patient
487
+ def localise_remote_patient(patient)
488
+ Patient.new(
489
+ patient_identifiers: localise_remote_patient_identifiers(patient),
490
+ person: Person.new(
491
+ names: localise_remote_patient_names(patient),
492
+ addresses: localise_remote_patient_addresses(patient),
493
+ birthdate: patient['birthdate'],
494
+ birthdate_estimated: patient['birthdate_estimated'],
495
+ gender: patient['gender']
496
+ )
497
+ )
498
+ end
499
+
500
+ def localise_remote_patient_identifiers(remote_patient)
501
+ [PatientIdentifier.new(identifier: remote_patient['npid'],
502
+ identifier_type: patient_identifier_type('National ID')),
503
+ PatientIdentifier.new(identifier: remote_patient['doc_id'],
504
+ identifier_type: patient_identifier_type('Dde Person Document ID'))]
505
+ end
506
+
507
+ def localise_remote_patient_names(remote_patient)
508
+ [PersonName.new(given_name: remote_patient['given_name'],
509
+ family_name: remote_patient['family_name'],
510
+ middle_name: remote_patient['middle_name'])]
511
+ end
512
+
513
+ def localise_remote_patient_addresses(remote_patient)
514
+ address = PersonAddress.new
515
+ address.city_village = remote_patient['attributes']['home_village']
516
+ address.home_traditional_authority = remote_patient['attributes']['home_traditional_authority']
517
+ address.home_district = remote_patient['attributes']['home_district']
518
+ address.current_village = remote_patient['attributes']['current_village']
519
+ address.current_traditional_authority = remote_patient['attributes']['current_traditional_authority']
520
+ address.current_district = remote_patient['attributes']['current_district']
521
+ [address]
522
+ end
523
+
524
+ def dde_client
525
+ client = DdeClient::DdeClient.new
526
+
527
+ connection = dde_connections[visit_type.id]
528
+
529
+ @dde_connections[visit_type.id] = if connection && %i[url username password].all? { |key| connection&.key?(key) }
530
+ client.restore_connection(connection)
531
+ else
532
+ client.connect(url: dde_config[:url],
533
+ username: dde_config[:username],
534
+ password: dde_config[:password])
535
+ end
536
+
537
+ client
538
+ end
539
+
540
+ # Loads a dde client into the dde_clients_cache for the
541
+ def dde_config
542
+ main_config = YAML.load_file(Dde_CONFIG_PATH)['config']
543
+ raise 'No configuration for Dde found' unless main_config
544
+
545
+ visit_type_config = main_config[visit_type.name.downcase]
546
+ raise "No Dde config for visit_type #{visit_type.name.downcase} found" unless visit_type_config
547
+
548
+ {
549
+ url: main_config['url'],
550
+ username: visit_type_config['username'],
551
+ password: visit_type_config['password']
552
+ }
553
+ end
554
+
555
+ # Converts an openmrs patient structure to a Dde person structure
556
+ def openmrs_to_dde_patient(patient)
557
+ LOGGER.debug "Converting OpenMRS person to dde_patient: #{patient}"
558
+ person = patient.person
559
+
560
+ person_name = person.names[0]
561
+ person_address = person.addresses[0]
562
+ person_attributes = filter_person_attributes(person.person_attributes)
563
+
564
+ dde_patient = HashWithIndifferentAccess.new(
565
+ given_name: person_name.given_name,
566
+ family_name: person_name.family_name,
567
+ gender: person.gender&.first,
568
+ birthdate: person.birthdate,
569
+ birthdate_estimated: person.birthdate_estimated ? 1 : 0,
570
+ attributes: {
571
+ current_district: person_address ? person_address.address3 : nil,
572
+ current_traditional_authority: person_address ? person_address.state_province : nil,
573
+ current_village: person_address ? person_address.city_village : nil,
574
+ home_district: person_address ? person_address.address1 : nil,
575
+ home_village: person_address ? person_address.city_village : nil,
576
+ home_traditional_authority: person_address ? person_address.county_district : nil,
577
+ occupation: person_attributes ? person_attributes[:occupation] : nil
578
+ }
579
+ )
580
+
581
+ doc_id = patient.patient_identifiers.where(identifier_type: patient_identifier_type('Dde person document id')).first
582
+ dde_patient[:doc_id] = doc_id.identifier if doc_id
583
+
584
+ LOGGER.debug "Converted openmrs person to dde_patient: #{dde_patient}"
585
+ dde_patient
586
+ end
587
+
588
+ def filter_person_attributes(person_attributes)
589
+ return nil unless person_attributes
590
+
591
+ person_attributes.each_with_object({}) do |attr, filtered|
592
+ case attr.person_attribute_type.name.downcase.gsub(/\s+/, '_')
593
+ when 'cell_phone_number'
594
+ filtered[:cell_phone_number] = attr.value
595
+ when 'occupation'
596
+ filtered[:occupation] = attr.value
597
+ when 'birthplace'
598
+ filtered[:home_district] = attr.value
599
+ when 'home_village'
600
+ filtered[:home_village] = attr.value
601
+ when 'ancestral_traditional_authority'
602
+ filtered[:home_traditional_authority] = attr.value
603
+ end
604
+ end
605
+ end
606
+
607
+ def patient_doc_id(patient)
608
+ PatientIdentifier
609
+ .joins(:identifier_type)
610
+ .merge(PatientIdentifierType.where(name: 'Dde person document id'))
611
+ .where(patient: patient)
612
+ .first
613
+ &.identifier
614
+ end
615
+
616
+ def dde_doc_id_type
617
+ PatientIdentifierType.find_by_name('Dde Person document ID')
618
+ end
619
+
620
+ def find_patients_by_doc_id(doc_ids)
621
+ identifiers = PatientIdentifier.joins(:identifier_type)
622
+ .merge(PatientIdentifierType.where(name: 'Dde Person Document ID'))
623
+ .where(identifier: doc_ids)
624
+ Patient.joins(:identifiers).merge(identifiers).distinct
625
+ end
626
+
627
+ def person_service
628
+ PersonService.new
629
+ end
630
+
631
+ def patient_service
632
+ PatientService.new
633
+ end
634
+
635
+ def merging_service
636
+ DdeClient::MergingService.new(self, -> { dde_client })
637
+ end
638
+
639
+ # A cache for all connections to dde (indexed by visit_type id)
640
+ def dde_connections
641
+ @dde_connections ||= {}
642
+ end
643
+ end