dde_mahis 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ # module DdeMahis
4
+ ##
5
+ # Matches local and remote (DdeMahis) people.
6
+ #
7
+ # TODO: Move module to own file
8
+ class Matcher
9
+ class << self
10
+ def find_differences(local_person, remote_person)
11
+ FIELDS_TO_MATCH
12
+ .map { |field| [field, diff_field(field, local_person, remote_person)] }
13
+ .reject { |_field, diff| diff.nil? }
14
+ .each_with_object({}) { |sub_diff, diff| diff[sub_diff[0]] = sub_diff[1] }
15
+ end
16
+
17
+ private
18
+
19
+ FIELDS_TO_MATCH = %w[given_name family_name birthdate birthdate_estimated gender
20
+ current_village current_traditional_authority current_district
21
+ home_village home_traditional_authority home_district
22
+ npid].freeze
23
+
24
+ LOCATION_FIELDS = %w[current_village current_traditional_authority current_district
25
+ home_village home_traditional_authority home_district].freeze
26
+
27
+ def diff_field(field, local_person, remote_person)
28
+ return diff_location(field, local_person, remote_person) if LOCATION_FIELDS.include?(field)
29
+
30
+ send("diff_#{field}", local_person, remote_person)
31
+ end
32
+
33
+ def diff_given_name(local_person, remote_person)
34
+ local_name = PersonName.find_by_person_id(local_person.person_id)
35
+
36
+ return nil if local_name&.given_name&.casecmp?(remote_person['given_name'])
37
+
38
+ { local: local_name.given_name, remote: remote_person['given_name'] }
39
+ end
40
+
41
+ def diff_family_name(local_person, remote_person)
42
+ local_name = PersonName.find_by_person_id(local_person.person_id)
43
+
44
+ return nil if local_name&.family_name&.casecmp?(remote_person['family_name'])
45
+
46
+ { local: local_name.family_name, remote: remote_person['family_name'] }
47
+ end
48
+
49
+ def diff_birthdate(local_person, remote_person)
50
+ return nil if local_person.birthdate&.to_date == remote_person['birthdate'].to_date
51
+
52
+ { local: local_person.birthdate, remote: remote_person['birthdate'] }
53
+ end
54
+
55
+ def diff_birthdate_estimated(local_person, remote_person)
56
+ birthdate_estimated = local_person.birthdate_estimated.positive?
57
+
58
+ return nil if birthdate_estimated == remote_person['birthdate_estimated']
59
+
60
+ { local: birthdate_estimated, remote: remote_person['birthdate_estimated'] }
61
+ end
62
+
63
+ def diff_gender(local_person, remote_person)
64
+ return nil if local_person.gender.first.casecmp?(remote_person['gender'].first)
65
+
66
+ { local: local_person.gender, remote: remote_person['gender'] }
67
+ end
68
+
69
+ def diff_npid(local_person, remote_person)
70
+ npid_type = PatientIdentifierType.where(name: 'National id')
71
+ local_npid = PatientIdentifier.find_by(patient_id: local_person.person_id, identifier_type: npid_type)&.identifier
72
+
73
+ return nil if local_npid&.casecmp?(remote_person['npid'])
74
+
75
+ { local: local_npid, remote: remote_person['npid'] }
76
+ end
77
+
78
+ def diff_location(field, local_person, remote_person)
79
+ local_address = PersonAddress.find_by_person_id(local_person.person_id)
80
+ remote_address = remote_person['attributes']
81
+
82
+ return nil if local_address.send(field).casecmp?(remote_address[field])
83
+ { local: local_address.send(field), remote: remote_address[field] }
84
+ end
85
+
86
+ def current_location_id
87
+ Location.current_health_center.location_id
88
+ end
89
+ end
90
+ end
91
+
92
+ # end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This service will handle merge audits with their tree structure
4
+ class MergeAuditService
5
+ # This method a merge audit for us
6
+ def create_merge_audit(primary_patient, secondary_patient, merge_type)
7
+ recent_merge_id = MergeAudit.where(primary_id: secondary_patient).last&.id
8
+ merge_audit = MergeAudit.create({ primary_id: primary_patient, secondary_id: secondary_patient,
9
+ creator: User.current.id, merge_type: merge_type,
10
+ secondary_previous_merge_id: recent_merge_id })
11
+ raise "Could not create audit trail due to #{merge_audit.errors.as_json}" unless merge_audit.errors.empty?
12
+ end
13
+
14
+ # this uses the patient identifier to get the audit tree
15
+ def get_patient_audit(identifier)
16
+ fetch_merge_audit(find_voided_identifier(identifier))
17
+ end
18
+
19
+ # this uses the patient id to get the audit tree and it is used by get patient_audit
20
+ def fetch_merge_audit(secondary)
21
+ first_merge = common_merge_fetch('ma.secondary_id', secondary)
22
+ raise NotFoundError, "There is no merge for #{secondary}" if first_merge.blank?
23
+
24
+ count = 0
25
+ tree = [first_merge.merge({ 'merge_number' => count += 1 })]
26
+ merge_id = MergeAudit.where(primary_id: first_merge['primary_id']).last&.id
27
+ until merge_id.blank?
28
+ parent = common_merge_fetch('ma.secondary_previous_merge_id', merge_id)
29
+ tree << parent.merge({ 'merge_number' => count += 1 }) unless parent.blank?
30
+ merge_id = parent.blank? ? nil : MergeAudit.where(primary_id: parent['primary_id']).last&.id
31
+ end
32
+ tree.reverse
33
+ end
34
+
35
+ def common_merge_fetch(field, fetch_value)
36
+ ActiveRecord::Base.connection.select_one <<~SQL
37
+ SELECT ma.id, ma.primary_id, ma.secondary_id, ma.created_at merge_date, ma.merge_type, pn.given_name primary_first_name, pn.family_name primary_surname, p.gender primary_gender, p.birthdate primary_birthdate,
38
+ spn.given_name secondary_first_name, spn.family_name secondary_surname, sp.gender secondary_gender, sp.birthdate secondary_birthdate
39
+ FROM merge_audits ma
40
+ INNER JOIN person_name pn ON pn.person_id = ma.primary_id
41
+ INNER JOIN person p ON p.person_id = ma.primary_id
42
+ INNER JOIN person_name spn ON spn.person_id = ma.secondary_id AND spn.voided = 1
43
+ INNER JOIN person sp ON sp.person_id = ma.secondary_id AND sp.voided = 1
44
+ WHERE #{field} = #{fetch_value} AND ma.voided = 0
45
+ SQL
46
+ end
47
+
48
+ def find_voided_identifier(identifier)
49
+ result = ActiveRecord::Base.connection.select_one <<~SQL
50
+ SELECT patient_id FROM patient_identifier WHERE identifier = '#{identifier}' AND identifier_type = 3 AND voided = 1 ORDER BY date_voided ASC
51
+ SQL
52
+ raise NotFoundError, "Failed to find voided identifier: #{identifier}" if result.blank?
53
+
54
+ result['patient_id']
55
+ end
56
+ end