fhir_scorecard 1.0

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,73 @@
1
+ module FHIR
2
+ class RxNormMeds < FHIR::Rubrics
3
+
4
+ # Medications should be coded with RxNorm
5
+ rubric :rxnorm_meds do |record|
6
+ results = {
7
+ :eligible_fields => 0,
8
+ :validated_fields => 0
9
+ }
10
+ # Medication.code (CodeableConcept)
11
+ # MedicationAdministration.medicationCodeableConcept / medicationReference
12
+ # MedicationDispense.medicationCodeableConcept / medicationReference
13
+ # MedicationOrder.medicationCodeableConcept / medicationReference
14
+ # MedicationStatement.medicationCodeableConcept / medicationReference
15
+
16
+ resources = record.entry.map{|e|e.resource}
17
+ resources.each do |resource|
18
+ if resource.is_a?(FHIR::Medication)
19
+ results[:eligible_fields] += 1
20
+ results[:validated_fields] += 1 if rxnorm?(resource.code)
21
+ elsif (
22
+ resource.is_a?(FHIR::MedicationOrder) ||
23
+ resource.is_a?(FHIR::MedicationDispense) ||
24
+ resource.is_a?(FHIR::MedicationAdministration) ||
25
+ resource.is_a?(FHIR::MedicationStatement) )
26
+ if resource.medicationCodeableConcept
27
+ results[:eligible_fields] += 1
28
+ results[:validated_fields] += 1 if rxnorm?(resource.medicationCodeableConcept)
29
+ elsif resource.medicationReference
30
+ results[:eligible_fields] += 1
31
+ results[:validated_fields] += 1 if local_rxnorm_reference?(resource.medicationReference,record,resource.contained)
32
+ end
33
+ end
34
+ end
35
+
36
+ percentage = results[:validated_fields].to_f / results[:eligible_fields].to_f
37
+ percentage = 0.0 if percentage.nan?
38
+ points = 10.0 * percentage
39
+ message = "#{(100 * percentage).to_i}% (#{results[:validated_fields]}/#{results[:eligible_fields]}) of Medication[x] Resource codes use RxNorm. Maximum of 10 points."
40
+ response(points.to_i,message)
41
+ end
42
+
43
+ def self.rxnorm?(codeableconcept)
44
+ return false if codeableconcept.nil? || codeableconcept.coding.nil?
45
+ codeableconcept.coding.any?{|x| x.system=='http://www.nlm.nih.gov/research/umls/rxnorm' && FHIR::Terminology.get_description('RXNORM',x.code)}
46
+ end
47
+
48
+ def self.local_rxnorm_reference?(reference,record,contained)
49
+ if contained && reference.reference && reference.reference.start_with?('#')
50
+ contained.each do |resource|
51
+ return true if resource.is_a?(FHIR::Medication) && reference.reference[1..-1]==resource.id
52
+ end
53
+ end
54
+ record.entry.each do |entry|
55
+ if entry.resource.is_a?(FHIR::Medication) && reference_matchs?(reference,entry)
56
+ return true
57
+ end
58
+ end
59
+ false
60
+ end
61
+
62
+ def self.reference_matchs?(reference,entry)
63
+ if reference.reference.start_with?('urn:uuid:')
64
+ (reference.reference == entry.fullUrl)
65
+ elsif reference.reference.include?('Medication/')
66
+ (reference.reference.split('Medication/').last.split('/').first == entry.id)
67
+ else
68
+ false # unable to verify reference points to RXNORM
69
+ end
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,48 @@
1
+ module FHIR
2
+ class SmokingStatus < FHIR::Rubrics
3
+
4
+ SMOKING_CODES = [
5
+ '449868002', #Current every day smoker
6
+ '428041000124106', #Current some day smoker
7
+ '8517006', #Former smoker
8
+ '266919005', #Never smoker
9
+ '77176002', #Smoker, current status unknown
10
+ '266927001', #Unknown if ever smoked
11
+ '428071000124103', #Current Heavy tobacco smoker
12
+ '428061000124105' #Current Light tobacco smoker
13
+ ]
14
+
15
+ # The Patient Record should include Smoking Status
16
+ rubric :smoking_status do |record|
17
+
18
+ resources = record.entry.map{|e|e.resource}
19
+ has_smoking_status = resources.any? do |resource|
20
+ resource.is_a?(FHIR::Observation) && smoking_observation?(resource)
21
+ end
22
+
23
+ if has_smoking_status
24
+ points = 10
25
+ else
26
+ points = 0
27
+ end
28
+
29
+ message = "The Patient Record should include Smoking Status (DAF-core-smokingstatus profile on Observation)."
30
+ response(points.to_i,message)
31
+ end
32
+
33
+ def self.smoking_observation?(resource)
34
+ smoking_code?(resource.code) && smoking_value?(resource.valueCodeableConcept)
35
+ end
36
+
37
+ def self.smoking_code?(codeableconcept)
38
+ return false if codeableconcept.nil? || codeableconcept.coding.nil?
39
+ codeableconcept.coding.any?{|x| x.system=='http://loinc.org' && x.code=='72166-2'}
40
+ end
41
+
42
+ def self.smoking_value?(codeableconcept)
43
+ return false if codeableconcept.nil? || codeableconcept.coding.nil?
44
+ codeableconcept.coding.any?{|x| x.system=='http://snomed.info/sct' && SMOKING_CODES.include?(x.code)}
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,32 @@
1
+ module FHIR
2
+ class SnomedConditions < FHIR::Rubrics
3
+
4
+ # Conditions should be coded with the SNOMED Core Subset
5
+ rubric :snomed_core do |record|
6
+ results = {
7
+ :eligible_fields => 0,
8
+ :validated_fields => 0
9
+ }
10
+
11
+ resources = record.entry.map{|e|e.resource}
12
+ resources.each do |resource|
13
+ if resource.is_a?(FHIR::Condition)
14
+ results[:eligible_fields] += 1
15
+ results[:validated_fields] += 1 if snomed_core?(resource.code)
16
+ end
17
+ end
18
+
19
+ percentage = results[:validated_fields].to_f / results[:eligible_fields].to_f
20
+ percentage = 0.0 if percentage.nan?
21
+ points = 10.0 * percentage
22
+ message = "#{(100 * percentage).to_i}% (#{results[:validated_fields]}/#{results[:eligible_fields]}) of Condition Resource codes use the SNOMED Core Subset. Maximum of 10 points."
23
+ response(points.to_i,message)
24
+ end
25
+
26
+ def self.snomed_core?(codeableconcept)
27
+ return false if codeableconcept.nil? || codeableconcept.coding.nil?
28
+ codeableconcept.coding.any?{|x| x.system=='http://snomed.info/sct' && FHIR::Terminology.is_core_snomed?(x.code)}
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,64 @@
1
+ module FHIR
2
+ class VitalSigns < FHIR::Rubrics
3
+
4
+ # http://hl7.org/fhir/2016Sep/observation-vitalsigns.html
5
+ VITAL_SIGNS = {
6
+ # '8716-3' => '', # vital signs group
7
+ '9279-1' => [], #'/min', # respitory rate
8
+ '8867-4' => [], #'/min', # heart rate
9
+ '59408-5' => [], #'%', # Oxygen saturation
10
+ '8310-5' => [], #'Cel', # Body temperature
11
+ '8302-2' => [], #'cm', # Body height
12
+ '9843-4' => [], #'cm', # Head circumference
13
+ '29463-7' => [], #'kg', # Body weight
14
+ '39156-5' => [], #'kg/m2', # Body mass index
15
+ '55284-4' => ['8480-6','8462-4'] # Blood pressure systolic and diastolic group
16
+ # '8480-6' => 'mm[Hg]', # Systolic blood pressure
17
+ # '8462-4' => 'mm[Hg]', # Diastolic blood pressure
18
+ # '8478-0' => 'mm[Hg]' # Mean blood pressure
19
+ }
20
+
21
+ # Vital Signs should be present with specific LOINC codes
22
+ rubric :vital_signs do |record|
23
+
24
+ not_found = VITAL_SIGNS.clone
25
+
26
+ resources = record.entry.map{|e|e.resource}
27
+ resources.each do |resource|
28
+ if resource.is_a?(FHIR::Observation)
29
+ found_code = get_vital_code(resource.code)
30
+ components = not_found[ found_code ]
31
+ if components.nil?
32
+ components = []
33
+ else
34
+ components = components.clone
35
+ end
36
+ if components.empty?
37
+ not_found.delete(found_code)
38
+ elsif resource.component
39
+ resource.component.each do |component|
40
+ sub_code = get_vital_code(component.code)
41
+ components.delete(sub_code)
42
+ end
43
+ not_found.delete(found_code) if components.empty?
44
+ end
45
+ end
46
+ end
47
+
48
+ percentage = ( (VITAL_SIGNS.length.to_f - not_found.length.to_f) / (VITAL_SIGNS.length.to_f) )
49
+ percentage = 0.0 if percentage.nan?
50
+ points = 10.0 * percentage
51
+ message = "#{(100 * percentage).to_i}% (#{VITAL_SIGNS.length - not_found.length}/#{VITAL_SIGNS.length}) of Vital Signs had at least one recorded Observation. Maximum of 10 points."
52
+ response(points.to_i,message)
53
+ end
54
+
55
+ def self.get_vital_code(codeableconcept)
56
+ return nil if codeableconcept.nil? || codeableconcept.coding.nil?
57
+
58
+ coding = codeableconcept.coding.find{|x| x.system=='http://loinc.org' && VITAL_SIGNS.has_key?(x.code)}
59
+ code = coding.code if coding
60
+ code
61
+ end
62
+
63
+ end
64
+ end
data/lib/rubrics.rb ADDED
@@ -0,0 +1,23 @@
1
+ module FHIR
2
+ class Rubrics
3
+
4
+ @@rubrics = {}
5
+
6
+ def self.apply(record)
7
+ report = {}
8
+ @@rubrics.each do |key,rubric|
9
+ report[key] = rubric.call(record)
10
+ end
11
+ report
12
+ end
13
+
14
+ def self.rubric(key,&block)
15
+ @@rubrics[key] = block
16
+ end
17
+
18
+ def self.response(points,message)
19
+ {:points=>points,:message=>message}
20
+ end
21
+
22
+ end
23
+ end
data/lib/scorecard.rb ADDED
@@ -0,0 +1,46 @@
1
+ module FHIR
2
+ class Scorecard
3
+
4
+ attr_accessor :report
5
+ attr_accessor :points
6
+
7
+ def score(bundle_raw)
8
+ @report = {}
9
+ @points = 0
10
+
11
+ # Check that the patient record is a FHIR Bundle.
12
+ bundle = FHIR.from_contents(bundle_raw)
13
+ if bundle.is_a?(FHIR::Bundle)
14
+ @points += 10
15
+ @report[:bundle] = { :points=>10, :message=>'Patient Record is a FHIR Bundle.'}
16
+ else
17
+ @report[:bundle] = { :points=>0, :message=>'Patient Record must be a FHIR Bundle.'}
18
+ end
19
+
20
+ if bundle.is_a?(FHIR::Bundle)
21
+
22
+ # Check that the patient record contains a FHIR Patient.
23
+ @patient = nil
24
+ count = 0
25
+ bundle.entry.each do |entry|
26
+ if entry.resource && entry.resource.is_a?(FHIR::Patient)
27
+ @patient = entry.resource
28
+ count += 1
29
+ end
30
+ end
31
+ if @patient && count==1
32
+ @points += 10
33
+ @report[:patient] = { :points=>10, :message=>'Patient Record contains one FHIR Patient.'}
34
+ else
35
+ @report[:patient] = { :points=>0, :message=>'Patient Record must contain one FHIR Patient.'}
36
+ end
37
+
38
+ report.merge!(FHIR::Rubrics.apply(bundle))
39
+ end
40
+
41
+ @report[:points] = @report.values.inject(0){|sum,section| sum+=section[:points]}
42
+ @report
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,207 @@
1
+ namespace :fhir do
2
+
3
+ desc 'console'
4
+ task :console, [] do |t, args|
5
+ binding.pry
6
+ end
7
+
8
+ desc 'score a FHIR Bundle'
9
+ task :score, [:bundle_path] do |t, args|
10
+ bundle_path = args[:bundle_path]
11
+ if bundle_path.nil?
12
+ puts 'A path to FHIR Bundle is required!'
13
+ else
14
+ contents = File.open(bundle_path,'r:UTF-8',&:read)
15
+ scorecard = FHIR::Scorecard.new
16
+ report = scorecard.score(contents)
17
+ puts
18
+ puts " POINTS CATEGORY MESSAGE"
19
+ puts " ------ -------- -------"
20
+ report.each do |key,value|
21
+ next if key==:points
22
+ printf(" %3d %20s %s\n", value[:points], key, value[:message])
23
+ end
24
+ puts " ------"
25
+ printf(" %3d %20s\n", report[:points], 'TOTAL')
26
+ puts
27
+ end
28
+ end
29
+
30
+ desc 'post-process LOINC Top 2000 common lab results CSV'
31
+ task :process_loinc, [] do |t, args|
32
+ require 'find'
33
+ require 'csv'
34
+ puts 'Looking for `./terminology/LOINC*.csv`...'
35
+ loinc_file = Find.find('terminology').find{|f| /LOINC.*\.csv$/ =~f }
36
+ if loinc_file
37
+ output_filename = 'terminology/scorecard_loinc_2000.txt'
38
+ puts "Writing to #{output_filename}..."
39
+ output = File.open(output_filename,'w:UTF-8')
40
+ line = 0
41
+ begin
42
+ CSV.foreach(loinc_file, encoding: 'iso-8859-1:utf-8', headers: true) do |row|
43
+ line += 1
44
+ next if row.length <=1 || row[1].nil? # skip the categories
45
+ # CODE | DESC | UCUM UNITS
46
+ output.write("#{row[1]}|#{row[2]}|#{row[6]}\n")
47
+ end
48
+ rescue Exception => e
49
+ puts "Error at line #{line}"
50
+ puts e.message
51
+ end
52
+ output.close
53
+ puts 'Done.'
54
+ else
55
+ puts 'LOINC file not found.'
56
+ puts 'Download the LOINC Top 2000 Common Lab Results file'
57
+ puts ' -> http://loinc.org/usage/obs/loinc-top-2000-plus-loinc-lab-observations-us.csv'
58
+ puts 'copy it into your `./terminology` folder, and rerun this task.'
59
+ end
60
+ end
61
+
62
+ desc 'post-process SNOMED Core Subset file'
63
+ task :process_snomed, [] do |t, args|
64
+ require 'find'
65
+ puts 'Looking for `./terminology/SNOMEDCT_CORE_SUBSET*.txt`...'
66
+ snomed_file = Find.find('terminology').find{|f| /SNOMEDCT_CORE_SUBSET.*\.txt$/ =~f }
67
+ if snomed_file
68
+ output_filename = 'terminology/scorecard_snomed_core.txt'
69
+ output = File.open(output_filename,'w:UTF-8')
70
+ line = 0
71
+ begin
72
+ entire_file = File.read(snomed_file)
73
+ puts "Writing to #{output_filename}..."
74
+ entire_file.split("\n").each do |l|
75
+ row = l.split('|')
76
+ line += 1
77
+ next if line==1 # skip the headers
78
+ # CODE | DESC
79
+ output.write("#{row[0]}|#{row[1]}\n")
80
+ end
81
+ rescue Exception => e
82
+ puts "Error at line #{line}"
83
+ puts e.message
84
+ end
85
+ output.close
86
+ puts 'Done.'
87
+ else
88
+ puts 'SNOMEDCT file not found.'
89
+ puts 'Download the SNOMEDCT Core Subset file'
90
+ puts ' -> https://www.nlm.nih.gov/research/umls/Snomed/core_subset.html'
91
+ puts 'copy it into your `./terminology` folder, and rerun this task.'
92
+ end
93
+ end
94
+
95
+ desc 'post-process common UCUM codes'
96
+ task :process_ucum, [] do |t, args|
97
+ require 'find'
98
+ puts 'Looking for `./terminology/concepts.tsv`...'
99
+ ucum_file = Find.find('terminology').find{|f| /concepts.tsv$/ =~f }
100
+ if ucum_file
101
+ output_filename = 'terminology/scorecard_ucum.txt'
102
+ output = File.open(output_filename,'w:UTF-8')
103
+ line = 0
104
+ begin
105
+ entire_file = File.read(ucum_file)
106
+ puts "Writing to #{output_filename}..."
107
+ entire_file.split("\n").each do |l|
108
+ row = l.split("\t")
109
+ line += 1
110
+ next if line==1 # skip the headers
111
+ output.write("#{row[0]}\n") # code
112
+ output.write("#{row[5]}\n") if row[0]!=row[5] # synonym
113
+ end
114
+ rescue Exception => e
115
+ puts "Error at line #{line}"
116
+ puts e.message
117
+ end
118
+ output.close
119
+ puts 'Done.'
120
+ else
121
+ puts 'UCUM concepts file not found.'
122
+ puts 'Download the UCUM concepts file'
123
+ puts ' -> http://download.hl7.de/documents/ucum/concepts.tsv'
124
+ puts 'copy it into your `./terminology` folder, and rerun this task.'
125
+ end
126
+ end
127
+
128
+ desc 'post-process UMLS terminology file'
129
+ task :process_umls, [] do |t, args|
130
+ require 'find'
131
+ puts 'Looking for `./terminology/MRCONSO.RRF`...'
132
+ input_file = Find.find('terminology').find{|f| f=='terminology/MRCONSO.RRF' }
133
+ if input_file
134
+ start = Time.now
135
+ output_filename = 'terminology/scorecard_umls.txt'
136
+ output = File.open(output_filename,'w:UTF-8')
137
+ line = 0
138
+ excluded = 0
139
+ excluded_systems = Hash.new(0)
140
+ begin
141
+ entire_file = File.read(input_file)
142
+ puts "Writing to #{output_filename}..."
143
+ entire_file.split("\n").each do |l|
144
+ row = l.split('|')
145
+ line += 1
146
+ include_code = false
147
+ codeSystem = row[11]
148
+ code = row[13]
149
+ description = row[14]
150
+ case codeSystem
151
+ when 'SNOMEDCT_US'
152
+ codeSystem = 'SNOMED'
153
+ include_code = (row[4]=='PF' && ['FN','OAF'].include?(row[12]))
154
+ when 'LNC'
155
+ codeSystem = 'LOINC'
156
+ include_code = true
157
+ when 'ICD10CM'
158
+ codeSystem = 'ICD10'
159
+ include_code = (row[12]=='PT')
160
+ when 'ICD10PCS'
161
+ codeSystem = 'ICD10'
162
+ include_code = (row[12]=='PT')
163
+ when 'ICD9CM'
164
+ codeSystem = 'ICD9'
165
+ include_code = (row[12]=='PT')
166
+ when 'MTHICD9'
167
+ codeSystem = 'ICD9'
168
+ include_code = true
169
+ when 'RXNORM'
170
+ include_code = true
171
+ when 'CVX'
172
+ include_code = (['PT','OP'].include?(row[12]))
173
+ when 'SRC'
174
+ # 'SRC' rows define the data sources in the file
175
+ include_code = false
176
+ else
177
+ include_code = false
178
+ excluded_systems[codeSystem] += 1
179
+ end
180
+ if include_code
181
+ output.write("#{codeSystem}|#{code}|#{description}\n")
182
+ else
183
+ excluded += 1
184
+ end
185
+ end
186
+ rescue Exception => e
187
+ puts "Error at line #{line}"
188
+ puts e.message
189
+ end
190
+ output.close
191
+ puts "Processed #{line} lines, excluding #{excluded} redundant entries."
192
+ puts "Excluded code systems: #{excluded_systems}" if !excluded_systems.empty?
193
+ finish = Time.now
194
+ minutes = ((finish-start)/60)
195
+ seconds = (minutes - minutes.floor) * 60
196
+ puts "Completed in #{minutes.floor} minute(s) #{seconds.floor} second(s)."
197
+ puts 'Done.'
198
+ else
199
+ puts 'UMLS file not found.'
200
+ puts 'Download the US National Library of Medicine (NLM) Unified Medical Language System (UMLS) Full Release files'
201
+ puts ' -> https://www.nlm.nih.gov/research/umls/licensedcontent/umlsknowledgesources.html'
202
+ puts 'Install the metathesaurus with the following data sources:'
203
+ puts ' CVX|CVX;ICD10CM|ICD10CM;ICD10PCS|ICD10PCS;ICD9CM|ICD9CM;LNC|LNC;MTHICD9|ICD9CM;RXNORM|RXNORM;SNOMEDCT_US|SNOMEDCT'
204
+ puts 'After installation, copy `{install path}/META/MRCONSO.RRF` into your `./terminology` folder, and rerun this task.'
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,135 @@
1
+ module FHIR
2
+ class Terminology
3
+
4
+ CODE_SYSTEMS = {
5
+ 'http://snomed.info/sct'=>'SNOMED',
6
+ 'http://loinc.org'=>'LOINC',
7
+ 'http://www.nlm.nih.gov/research/umls/rxnorm'=>'RXNORM',
8
+ 'http://hl7.org/fhir/sid/icd-10'=>'ICD10',
9
+ 'http://hl7.org/fhir/sid/icd-10-de'=>'ICD10',
10
+ 'http://hl7.org/fhir/sid/icd-10-nl'=>'ICD10',
11
+ 'http://hl7.org/fhir/sid/icd-10-us'=>'ICD10',
12
+ 'http://www.icd10data.com/icd10pcs'=>'ICD10',
13
+ 'http://hl7.org/fhir/sid/icd-9-cm'=>'ICD9',
14
+ 'http://hl7.org/fhir/sid/icd-9-cm/diagnosis'=>'ICD9',
15
+ 'http://hl7.org/fhir/sid/icd-9-cm/procedure'=>'ICD9',
16
+ 'http://hl7.org/fhir/sid/cvx'=>'CVX'
17
+ }
18
+
19
+ @@term_root = File.expand_path '../terminology',File.dirname(File.absolute_path(__FILE__))
20
+
21
+ @@loaded = false
22
+ @@top_lab_code_units = {}
23
+ @@top_lab_code_descriptions = {}
24
+ @@known_codes = {}
25
+ @@core_snomed = {}
26
+ @@common_ucum = []
27
+
28
+ def self.set_terminology_root(root)
29
+ @@term_root = root
30
+ end
31
+
32
+ def self.load_terminology
33
+ if !@@loaded
34
+ begin
35
+ # load the top lab codes
36
+ filename = File.join(@@term_root,'scorecard_loinc_2000.txt')
37
+ raw = File.open(filename,'r:UTF-8',&:read)
38
+ raw.split("\n").each do |line|
39
+ row = line.split('|')
40
+ @@top_lab_code_descriptions[row[0]] = row[1] if !row[1].nil?
41
+ @@top_lab_code_units[row[0]] = row[2] if !row[2].nil?
42
+ end
43
+ rescue Exception => error
44
+ FHIR.logger.error error
45
+ end
46
+
47
+ begin
48
+ # load the known codes
49
+ filename = File.join(@@term_root,'scorecard_umls.txt')
50
+ raw = File.open(filename,'r:UTF-8',&:read)
51
+ raw.split("\n").each do |line|
52
+ row = line.split('|')
53
+ codeSystem = row[0]
54
+ code = row[1]
55
+ description = row[2]
56
+ if @@known_codes[codeSystem]
57
+ codeSystemHash = @@known_codes[codeSystem]
58
+ else
59
+ codeSystemHash = {}
60
+ @@known_codes[codeSystem] = codeSystemHash
61
+ end
62
+ codeSystemHash[code] = description
63
+ end
64
+ rescue Exception => error
65
+ FHIR.logger.error error
66
+ end
67
+
68
+ begin
69
+ # load the core snomed codes
70
+ @@known_codes['SNOMED'] = {} if @@known_codes['SNOMED'].nil?
71
+ codeSystemHash = @@known_codes['SNOMED']
72
+ filename = File.join(@@term_root,'scorecard_snomed_core.txt')
73
+ raw = File.open(filename,'r:UTF-8',&:read)
74
+ raw.split("\n").each do |line|
75
+ row = line.split('|')
76
+ code = row[0]
77
+ description = row[1]
78
+ codeSystemHash[code] = description if codeSystemHash[code].nil?
79
+ @@core_snomed[code] = description
80
+ end
81
+ rescue Exception => error
82
+ FHIR.logger.error error
83
+ end
84
+
85
+ begin
86
+ # load common UCUM codes
87
+ filename = File.join(@@term_root,'scorecard_ucum.txt')
88
+ raw = File.open(filename,'r:UTF-8',&:read)
89
+ raw.split("\n").each do |code|
90
+ @@common_ucum << code
91
+ end
92
+ @@common_ucum.uniq!
93
+ rescue Exception => error
94
+ FHIR.logger.error error
95
+ end
96
+
97
+ @@loaded = true
98
+ end
99
+ end
100
+
101
+ def self.get_description(system,code)
102
+ load_terminology
103
+ if @@known_codes[system]
104
+ @@known_codes[system][code]
105
+ else
106
+ nil
107
+ end
108
+ end
109
+
110
+ def self.is_core_snomed?(code)
111
+ load_terminology
112
+ !@@core_snomed[code].nil?
113
+ end
114
+
115
+ def self.is_top_lab_code?(code)
116
+ load_terminology
117
+ !@@top_lab_code_units[code].nil?
118
+ end
119
+
120
+ def self.lab_units(code)
121
+ load_terminology
122
+ @@top_lab_code_units[code]
123
+ end
124
+
125
+ def self.is_known_ucum?(units)
126
+ load_terminology
127
+ @@top_lab_code_units.values.include?(units) || @@common_ucum.include?(units)
128
+ end
129
+
130
+ def self.lab_description(code)
131
+ load_terminology
132
+ @@top_lab_code_descriptions[code]
133
+ end
134
+ end
135
+ end
data/logs/.keep ADDED
File without changes
data/terminology/.keep ADDED
File without changes
@@ -0,0 +1,9 @@
1
+ require 'simplecov'
2
+ require_relative '../lib/fhir_scorecard'
3
+
4
+ require 'fhir_models'
5
+ require 'fileutils'
6
+ require 'pry'
7
+ require 'minitest'
8
+ require 'minitest/autorun'
9
+ require 'bundler/setup'