diff --git a/.gitignore b/.gitignore index f28dd49..0185f18 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ # Mac OS X FS Junk **/.DS_Store -output/ \ No newline at end of file +output/ +lib/validator_cli.jar diff --git a/README.md b/README.md index 37d655e..4394c61 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ This uscore-data-script requires working installations of Ruby and Java. After c ``` cd /uscore-data-script bundle install +get_validator.sh ``` ## Creating the Data Set @@ -22,7 +23,7 @@ The uscore-data-script will execute Synthea to generate synthetic patients, load FHIR Bundles, and select certain patients based on criteria described below. ``` -bundle exec ruby uscore-data-script.rb [mrburns] +bundle exec ruby uscore-data-script.rb [mrburns] [version] ``` There is an optional `mrburns` parameter, which if included, will generate a single longitudinal @@ -30,6 +31,9 @@ patient who possesses at least one resource conforming to every US Core profile. one patient with everything. If the `mrburns` parameter is not included, the script will generate a small collection of testing patients. +There is an optional `version` parameter. Legal values are `v3`, `v4`, and `v5` to specify a version of +the US Core Implementation Guide. The default value is `v4`. + 2. Use Data Use the data files located in `/output/data`. It includes @@ -73,7 +77,7 @@ on Pediatric BMI and Pediatric Weight, because the `Observation.value[x]` are re # License -Copyright 2020 The MITRE Corporation +Copyright 2020 - 2022 The MITRE Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/get_validator.sh b/get_validator.sh new file mode 100755 index 0000000..a090cc5 --- /dev/null +++ b/get_validator.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +#wget https://github.com/hapifhir/org.hl7.fhir.core/releases/download/5.6.54/validator_cli.jar --no-check-certificate +wget https://github.com/hapifhir/org.hl7.fhir.core/releases/download/5.6.54/validator_cli.jar --no-check-certificate +mv validator_cli.jar lib/validator_cli.jar diff --git a/lib/bulk_data_converter.rb b/lib/bulk_data_converter.rb index 8411d0e..f37e772 100644 --- a/lib/bulk_data_converter.rb +++ b/lib/bulk_data_converter.rb @@ -40,12 +40,12 @@ def convert_to_bulk_data(bundle, patient_json_override = nil) json = JSON.parse( entry.resource.to_json ) json = JSON.unparse(json) json = patient_json_override if index == 0 && patient_json_override + DataScript::Modifications.questionnaire_response_primitive_extension(json) if VERSION==5 && json.include?(DataScript::Modifications::QUESTIONNAIRE_PRAPARE) # rewrite all the references according to the keys keys.each do |key, value| json.gsub!(key, value) end - # json.gsub!('"value": "DATAABSENTREASONEXTENSIONGOESHERE"', "\"_value\": { \"extension\": [ #{DataScript::Modifications.data_absent_reason.to_json} ] }") file = open_file(entry.resource) file.write(json) diff --git a/lib/choice_type_creator.rb b/lib/choice_type_creator.rb index 414e27d..ce982ec 100644 --- a/lib/choice_type_creator.rb +++ b/lib/choice_type_creator.rb @@ -10,52 +10,8 @@ module DataScript # automatically generate. class ChoiceTypeCreator SWAP_COUNT = 1 - MUST_SUPPORT_CHOICE_TYPES = { - FHIR::DiagnosticReport => - { - prefix: 'effective', - suffixes: %w[DateTime Period], - profiles: [ - 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-note', - 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab' - ] - }, - FHIR::Immunization => - { - prefix: 'occurrence', - suffixes: %w[DateTime String], - profiles: [ - 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-immunization' - ] - }, - FHIR::Observation => - { - prefix: 'effective', - suffixes: %w[DateTime Period], - profiles: [ - 'http://hl7.org/fhir/StructureDefinition/resprate', - 'http://hl7.org/fhir/StructureDefinition/heartrate', - 'http://hl7.org/fhir/StructureDefinition/bodyweight', - 'http://hl7.org/fhir/StructureDefinition/bp', - 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus', - 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-weight-for-height', - 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab', - 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-bmi-for-age', - 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry', - 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile', - 'http://hl7.org/fhir/StructureDefinition/bodyheight', - 'http://hl7.org/fhir/StructureDefinition/bodytemp' - ] - }, - FHIR::Procedure => - { - prefix: 'performed', - suffixes: %w[DateTime Period], - profiles: [ - 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure' - ] - } - }.freeze + # MUST_SUPPORT_CHOICE_TYPES are defined in ./v3/choice_types.rb, ./v4/choice_types.rb, or ./v5/choice_types.rb + # Goes through the set of bundles, and ensure we have at least SWAP_COUNT resources # in each resource/profile pair listed above that conform to each choice type. @@ -90,15 +46,29 @@ def self.check_choice_types(bundles) # If we still have choice types left over, we need to add some of this type next if missing_attrs.empty? + # puts "Profile: #{profile_url}" missing_attrs.each do |missing_attr| + # puts " - Missing Attr: #{missing_attr}" # If there are more than (SWAP_COUNT * the number of choices) resources of this class # Swap the choice type on SWAP_COUNT to the new type profile_resources.select { |resource| missing_attrs.none? { |attribute| resource.send(attribute) } }.first(SWAP_COUNT).each do |resource| # Get the first non-nil attribute value - old_attr_type, old_attr_val = all_attrs.map do |a| - av = resource.send(a) - [a, av] if av - end.compact.first + old_attr_type = all_attrs.find do |type| + # puts " > Try #{type}" + !resource.send(type).nil? + end + # puts " >> Found #{old_attr_type}" + old_attr_val = nil + old_attr_val = resource.send(old_attr_type) unless old_attr_type.nil? + # puts " >>> Value #{old_attr_val}" + if old_attr_type.nil? || old_attr_val.nil? + # puts " >>>> This type is nil, try #{choice[:prefix]}" + old_attr_val = resource.send(choice[:prefix]) + # puts " >>>> Value #{old_attr_val}" + old_attr_type = old_attr_val.class.to_s.split('::').last + old_attr_type = "#{choice[:prefix]}#{old_attr_type}" + # puts " >>>> Assumed Type #{old_attr_type}" + end # convert it (using some ugly if statements) to the new type new_val = convert_choice_types(old_attr_val, missing_attr, old_attr_type) if profile_resources.count > (SWAP_COUNT * all_attrs.count) @@ -190,7 +160,7 @@ def self.datetime_plus_1_hour(datetime_string) # @param bundles [Array] an array of FHIR::Bundle objects # @return nil def self.add_to_correct_bundle(resource, bundles) - bundle = bundles.find { |b| DataScript::Constraints.patient(b).id == get_resource_patient_id(resource) } + bundle = bundles.find { |b| DataScript::Constraints.patient(b)&.id == get_resource_patient_id(resource) } entry = create_bundle_entry(resource) bundle.entry << entry provenance = bundle.entry.find { |e| e.resource.is_a? FHIR::Provenance }.resource diff --git a/lib/filter.rb b/lib/filter.rb new file mode 100644 index 0000000..1b56041 --- /dev/null +++ b/lib/filter.rb @@ -0,0 +1,216 @@ +require 'pry' + +module DataScript + class Filter + def self.filter!(bundles) + organization_bundle = bundles.find {|b| b.entry.first.resource.resourceType == 'Organization'} + practitioner_bundle = bundles.find {|b| b.entry.first.resource.resourceType == 'Practitioner'} + patient_bundles = bundles.select {|b| b.entry.first.resource.resourceType == 'Patient'} + + organizations_to_keep = [] + locations_to_keep = [] + practitioners_to_keep = [] + + patient_bundles.each do |bundle| + print "\n" + tik = Time.now.to_i + initial_length = bundle.entry.length + profile_coverage = [] + encounters_to_keep = [] + resources_to_keep = [] + + bundle.entry.reverse.each do |entry| + next unless ['AllergyIntolerance','Device','Encounter','Goal','RelatedPerson'].include?(entry.resource.resourceType) + # start by looking at Encounters, most but not all resources reference the Encounter... + if entry.resource.resourceType == 'Encounter' + print '.' + encounter_resources = get_resources_associated_with_encounter(bundle, entry.fullUrl) + encounter_resources_profiles = encounter_resources.map {|resource| resource&.meta&.profile&.first}.compact + if encounter_resources_profiles.any? {|p| !profile_coverage.include?(p) } + profile_coverage.append(encounter_resources_profiles).flatten! + profile_coverage.uniq! + encounters_to_keep << entry.resource + resources_to_keep.append(encounter_resources).flatten! + organizations_to_keep << entry.resource.serviceProvider + locations_to_keep << entry.resource.location&.first&.location + practitioners_to_keep << entry.resource.participant&.first&.individual + end + elsif entry.resource.resourceType == 'AllergyIntolerance' # allergyintolerance is special because it does not reference an encounter + print 'a' + resource_profile = entry.resource&.meta&.profile&.first + if resource_profile == 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-allergyintolerance' + resources_to_keep << entry.resource + profile_coverage.append(resource_profile).flatten! + profile_coverage.uniq! + end + elsif entry.resource.resourceType == 'Device' # device is special because it does not reference an encounter + print 'd' + resource_profile = entry.resource&.meta&.profile&.first + if resource_profile == 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-implantable-device' + resources_to_keep << entry.resource + profile_coverage.append(resource_profile).flatten! + profile_coverage.uniq! + end + elsif entry.resource.resourceType == 'Goal' # goal is special because it does not reference an encounter + print 'g' + resource_profile = entry.resource&.meta&.profile&.first + if resource_profile == 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-goal' + resources_to_keep << entry.resource + profile_coverage.append(resource_profile).flatten! + profile_coverage.uniq! + end + elsif entry.resource.resourceType == 'RelatedPerson' # relatedperson is special because it does not reference anything + print 'r' + resource_profile = entry.resource&.meta&.profile&.first + if resource_profile == 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-relatedperson' + resources_to_keep << entry.resource + profile_coverage.append(resource_profile).flatten! + profile_coverage.uniq! + end + end + end + + # get dangling encounters, e.g., medication X (prescribed in encounter A) with reason condition Y (diagnosed in encounter B) + resources_to_keep.each do |resource| + if resource.respond_to?(:encounter) + encounter_urn = resource.encounter&.reference + if encounter_urn + encounter = encounters_to_keep.find{|e| e.id == encounter_urn[9..-1]} + if encounter.nil? + print 'e' + entry = bundle.entry.find {|e| e.fullUrl == encounter_urn } + encounters_to_keep << entry.resource + end + end + end + end + + print "\n" + + bundle.entry.keep_if do |entry| + ['Patient','Provenance'].include?(entry.resource.resourceType) || + encounters_to_keep.include?(entry.resource) || + resources_to_keep.include?(entry.resource) + end + + provenance = bundle.entry.find {|e| e.resource.resourceType == 'Provenance' }.resource + uuids = bundle.entry.map {|e| e.fullUrl} + provenance.target.keep_if {|reference| uuids.include?(reference.reference) } + final_length = bundle.entry.length + tok = Time.now.to_i + puts " - Filtered #{initial_length} resources down to #{final_length} (#{DataScript::TimeUtilities.pretty(tok - tik)})." + end + + patient_bundles.each do |bundle| + bundle.entry.each do |entry| + if ['DiagnosticReport'].include?(entry.resource.resourceType) + entry.resource.performer.each do |performer| + performer = FHIR::Reference.new(performer) if performer.is_a?(Hash) + if performer.reference.start_with?('Practitioner') + practitioners_to_keep << performer + elsif performer.reference.start_with?('Organization') + organizations_to_keep << performer + end + end + end + end + end + + # array of references into uuids + organizations_to_keep.map! {|x| x.reference.split('|').last} + organizations_to_keep.uniq! + + # array of references into uuids + locations_to_keep.map! {|x| x.reference.split('|').last} + locations_to_keep.uniq! + + initial_length = organization_bundle.entry.length + organization_bundle.entry.keep_if do |entry| + organizations_to_keep.include?(entry.resource.id) || + locations_to_keep.include?(entry.resource.id) + end + final_length = organization_bundle.entry.length + puts " - Filtered #{initial_length} Organization and Location resources down to #{final_length}." + + # array of references into NPIs + practitioners_to_keep.map! {|x| x.reference.split('|').last} + practitioners_to_keep.uniq! + + initial_length = practitioner_bundle.entry.length + practitioner_bundle.entry.keep_if do |entry| + practitioners_to_keep.include?(entry.resource&.identifier&.first&.value) || + (entry.resource.respond_to?(:practitioner) && practitioners_to_keep.include?(entry.resource&.practitioner&.identifier&.value)) + end + final_length = practitioner_bundle.entry.length + puts " - Filtered #{initial_length} Practitioner and PractitionerRole resources down to #{final_length}." + end + + def self.get_resources_associated_with_encounter(bundle, encounter_urn) + resources = [] + refd_medications = [] + refd_conditions = [] + refd_related_person = [] + + bundle.entry.each do |entry| + if entry.resource.respond_to?(:encounter) && entry.resource.encounter&.reference == encounter_urn + resources << entry.resource + refd_medications << entry.resource.medicationReference&.reference if entry.resource.respond_to?(:medicationReference) + if entry.resource.respond_to?(:reasonReference) + refd_conditions << entry.resource.reasonReference&.first&.reference + # only keep the first reason... + entry.resource.reasonReference = [ entry.resource.reasonReference.first ] + end + refd_related_person << entry.resource&.participant&.find {|x| x&.role&.first&.text = 'Caregiver (person)'}&.member&.reference if entry.resource.resourceType == 'CareTeam' + elsif entry.resource.resourceType == 'DocumentReference' && entry.resource.context.encounter.first.reference == encounter_urn + resources << entry.resource + end + end + + refd_medications.uniq! + refd_conditions.uniq! + refd_related_person.uniq! + + refd_medications.each do |fullUrl| + resources << bundle.entry.find {|e| e.fullUrl == fullUrl}&.resource + end + + refd_conditions.each do |fullUrl| + resources << bundle.entry.find {|e| e.fullUrl == fullUrl}&.resource + end + + refd_related_person.each do |fullUrl| + resources << bundle.entry.find {|e| e.fullUrl == fullUrl}&.resource + end + + resources.uniq! + resources.compact! + resources + end + + def self.create_group(bundles) + group = FHIR::Group.new + group.id = SecureRandom.uuid + group.identifier = [ FHIR::Identifier.new ] + group.identifier.first.system = 'urn:ietf:rfc:3986' + group.identifier.first.value = "urn:uuid:#{group.id}" + group.active = true + group.type = 'person' + group.actual = true + group.name = 'Synthea US Core Patients' + patient_bundles = bundles.select {|b| b.entry.first.resource.resourceType == 'Patient'} + group.quantity = patient_bundles.length + group.member = [] + patient_bundles.each do |bundle| + group_member = FHIR::Group::Member.new + group_member.entity = FHIR::Reference.new + group_member.entity.reference = "Patient?identifier=https://github.com/synthetichealth/synthea|#{bundle.entry.first.resource.id}" + # group_member.entity.reference = bundle.entry.first.fullUrl + # group_member.entity.identifier = FHIR::Identifier.new + # group_member.entity.identifier.system = 'https://github.com/synthetichealth/synthea' + # group_member.entity.identifier.value = bundle.entry.first.resource.id + group.member << group_member + end + group + end + end +end diff --git a/lib/org.hl7.fhir.validator.jar b/lib/org.hl7.fhir.validator.jar deleted file mode 100644 index 16ac37c..0000000 Binary files a/lib/org.hl7.fhir.validator.jar and /dev/null differ diff --git a/lib/synthea/FastInfoset-1.2.13.jar b/lib/synthea_uscore_v3/FastInfoset-1.2.13.jar similarity index 100% rename from lib/synthea/FastInfoset-1.2.13.jar rename to lib/synthea_uscore_v3/FastInfoset-1.2.13.jar diff --git a/lib/synthea/ST4-4.0.8.jar b/lib/synthea_uscore_v3/ST4-4.0.8.jar similarity index 100% rename from lib/synthea/ST4-4.0.8.jar rename to lib/synthea_uscore_v3/ST4-4.0.8.jar diff --git a/lib/synthea/SimulationCoreLibrary_v1.5_slim.jar b/lib/synthea_uscore_v3/SimulationCoreLibrary_v1.5_slim.jar similarity index 100% rename from lib/synthea/SimulationCoreLibrary_v1.5_slim.jar rename to lib/synthea_uscore_v3/SimulationCoreLibrary_v1.5_slim.jar diff --git a/lib/synthea/accessors-smart-1.2.jar b/lib/synthea_uscore_v3/accessors-smart-1.2.jar similarity index 100% rename from lib/synthea/accessors-smart-1.2.jar rename to lib/synthea_uscore_v3/accessors-smart-1.2.jar diff --git a/lib/synthea/activation-1.1.1.jar b/lib/synthea_uscore_v3/activation-1.1.1.jar similarity index 100% rename from lib/synthea/activation-1.1.1.jar rename to lib/synthea_uscore_v3/activation-1.1.1.jar diff --git a/lib/synthea/animal-sniffer-annotations-1.17.jar b/lib/synthea_uscore_v3/animal-sniffer-annotations-1.17.jar similarity index 100% rename from lib/synthea/animal-sniffer-annotations-1.17.jar rename to lib/synthea_uscore_v3/animal-sniffer-annotations-1.17.jar diff --git a/lib/synthea/antlr-runtime-3.5.2.jar b/lib/synthea_uscore_v3/antlr-runtime-3.5.2.jar similarity index 100% rename from lib/synthea/antlr-runtime-3.5.2.jar rename to lib/synthea_uscore_v3/antlr-runtime-3.5.2.jar diff --git a/lib/synthea/antlr4-4.5.jar b/lib/synthea_uscore_v3/antlr4-4.5.jar similarity index 100% rename from lib/synthea/antlr4-4.5.jar rename to lib/synthea_uscore_v3/antlr4-4.5.jar diff --git a/lib/synthea/antlr4-runtime-4.5.jar b/lib/synthea_uscore_v3/antlr4-runtime-4.5.jar similarity index 100% rename from lib/synthea/antlr4-runtime-4.5.jar rename to lib/synthea_uscore_v3/antlr4-runtime-4.5.jar diff --git a/lib/synthea/asm-5.0.4.jar b/lib/synthea_uscore_v3/asm-5.0.4.jar similarity index 100% rename from lib/synthea/asm-5.0.4.jar rename to lib/synthea_uscore_v3/asm-5.0.4.jar diff --git a/lib/synthea/batik-anim-1.9.jar b/lib/synthea_uscore_v3/batik-anim-1.9.jar similarity index 100% rename from lib/synthea/batik-anim-1.9.jar rename to lib/synthea_uscore_v3/batik-anim-1.9.jar diff --git a/lib/synthea/batik-awt-util-1.9.jar b/lib/synthea_uscore_v3/batik-awt-util-1.9.jar similarity index 100% rename from lib/synthea/batik-awt-util-1.9.jar rename to lib/synthea_uscore_v3/batik-awt-util-1.9.jar diff --git a/lib/synthea/batik-bridge-1.9.jar b/lib/synthea_uscore_v3/batik-bridge-1.9.jar similarity index 100% rename from lib/synthea/batik-bridge-1.9.jar rename to lib/synthea_uscore_v3/batik-bridge-1.9.jar diff --git a/lib/synthea/batik-codec-1.9.jar b/lib/synthea_uscore_v3/batik-codec-1.9.jar similarity index 100% rename from lib/synthea/batik-codec-1.9.jar rename to lib/synthea_uscore_v3/batik-codec-1.9.jar diff --git a/lib/synthea/batik-constants-1.9.jar b/lib/synthea_uscore_v3/batik-constants-1.9.jar similarity index 100% rename from lib/synthea/batik-constants-1.9.jar rename to lib/synthea_uscore_v3/batik-constants-1.9.jar diff --git a/lib/synthea/batik-css-1.9.jar b/lib/synthea_uscore_v3/batik-css-1.9.jar similarity index 100% rename from lib/synthea/batik-css-1.9.jar rename to lib/synthea_uscore_v3/batik-css-1.9.jar diff --git a/lib/synthea/batik-dom-1.9.jar b/lib/synthea_uscore_v3/batik-dom-1.9.jar similarity index 100% rename from lib/synthea/batik-dom-1.9.jar rename to lib/synthea_uscore_v3/batik-dom-1.9.jar diff --git a/lib/synthea/batik-ext-1.9.jar b/lib/synthea_uscore_v3/batik-ext-1.9.jar similarity index 100% rename from lib/synthea/batik-ext-1.9.jar rename to lib/synthea_uscore_v3/batik-ext-1.9.jar diff --git a/lib/synthea/batik-gvt-1.9.jar b/lib/synthea_uscore_v3/batik-gvt-1.9.jar similarity index 100% rename from lib/synthea/batik-gvt-1.9.jar rename to lib/synthea_uscore_v3/batik-gvt-1.9.jar diff --git a/lib/synthea/batik-i18n-1.9.jar b/lib/synthea_uscore_v3/batik-i18n-1.9.jar similarity index 100% rename from lib/synthea/batik-i18n-1.9.jar rename to lib/synthea_uscore_v3/batik-i18n-1.9.jar diff --git a/lib/synthea/batik-parser-1.9.jar b/lib/synthea_uscore_v3/batik-parser-1.9.jar similarity index 100% rename from lib/synthea/batik-parser-1.9.jar rename to lib/synthea_uscore_v3/batik-parser-1.9.jar diff --git a/lib/synthea/batik-rasterizer-1.9.jar b/lib/synthea_uscore_v3/batik-rasterizer-1.9.jar similarity index 100% rename from lib/synthea/batik-rasterizer-1.9.jar rename to lib/synthea_uscore_v3/batik-rasterizer-1.9.jar diff --git a/lib/synthea/batik-script-1.9.jar b/lib/synthea_uscore_v3/batik-script-1.9.jar similarity index 100% rename from lib/synthea/batik-script-1.9.jar rename to lib/synthea_uscore_v3/batik-script-1.9.jar diff --git a/lib/synthea/batik-svg-dom-1.9.jar b/lib/synthea_uscore_v3/batik-svg-dom-1.9.jar similarity index 100% rename from lib/synthea/batik-svg-dom-1.9.jar rename to lib/synthea_uscore_v3/batik-svg-dom-1.9.jar diff --git a/lib/synthea/batik-svggen-1.9.jar b/lib/synthea_uscore_v3/batik-svggen-1.9.jar similarity index 100% rename from lib/synthea/batik-svggen-1.9.jar rename to lib/synthea_uscore_v3/batik-svggen-1.9.jar diff --git a/lib/synthea/batik-svgrasterizer-1.9.jar b/lib/synthea_uscore_v3/batik-svgrasterizer-1.9.jar similarity index 100% rename from lib/synthea/batik-svgrasterizer-1.9.jar rename to lib/synthea_uscore_v3/batik-svgrasterizer-1.9.jar diff --git a/lib/synthea/batik-transcoder-1.9.jar b/lib/synthea_uscore_v3/batik-transcoder-1.9.jar similarity index 100% rename from lib/synthea/batik-transcoder-1.9.jar rename to lib/synthea_uscore_v3/batik-transcoder-1.9.jar diff --git a/lib/synthea/batik-util-1.9.jar b/lib/synthea_uscore_v3/batik-util-1.9.jar similarity index 100% rename from lib/synthea/batik-util-1.9.jar rename to lib/synthea_uscore_v3/batik-util-1.9.jar diff --git a/lib/synthea/batik-xml-1.9.jar b/lib/synthea_uscore_v3/batik-xml-1.9.jar similarity index 100% rename from lib/synthea/batik-xml-1.9.jar rename to lib/synthea_uscore_v3/batik-xml-1.9.jar diff --git a/lib/synthea/biojava-ontology-4.0.0.jar b/lib/synthea_uscore_v3/biojava-ontology-4.0.0.jar similarity index 100% rename from lib/synthea/biojava-ontology-4.0.0.jar rename to lib/synthea_uscore_v3/biojava-ontology-4.0.0.jar diff --git a/lib/synthea/checker-qual-2.8.1.jar b/lib/synthea_uscore_v3/checker-qual-2.8.1.jar similarity index 100% rename from lib/synthea/checker-qual-2.8.1.jar rename to lib/synthea_uscore_v3/checker-qual-2.8.1.jar diff --git a/lib/synthea/commonj.sdo-2.1.1.jar b/lib/synthea_uscore_v3/commonj.sdo-2.1.1.jar similarity index 100% rename from lib/synthea/commonj.sdo-2.1.1.jar rename to lib/synthea_uscore_v3/commonj.sdo-2.1.1.jar diff --git a/lib/synthea/commons-beanutils-1.9.3.jar b/lib/synthea_uscore_v3/commons-beanutils-1.9.3.jar similarity index 100% rename from lib/synthea/commons-beanutils-1.9.3.jar rename to lib/synthea_uscore_v3/commons-beanutils-1.9.3.jar diff --git a/lib/synthea/commons-codec-1.12.jar b/lib/synthea_uscore_v3/commons-codec-1.12.jar similarity index 100% rename from lib/synthea/commons-codec-1.12.jar rename to lib/synthea_uscore_v3/commons-codec-1.12.jar diff --git a/lib/synthea/commons-collections-3.2.2.jar b/lib/synthea_uscore_v3/commons-collections-3.2.2.jar similarity index 100% rename from lib/synthea/commons-collections-3.2.2.jar rename to lib/synthea_uscore_v3/commons-collections-3.2.2.jar diff --git a/lib/synthea/commons-csv-1.5.jar b/lib/synthea_uscore_v3/commons-csv-1.5.jar similarity index 100% rename from lib/synthea/commons-csv-1.5.jar rename to lib/synthea_uscore_v3/commons-csv-1.5.jar diff --git a/lib/synthea/commons-digester-1.8.jar b/lib/synthea_uscore_v3/commons-digester-1.8.jar similarity index 100% rename from lib/synthea/commons-digester-1.8.jar rename to lib/synthea_uscore_v3/commons-digester-1.8.jar diff --git a/lib/synthea/commons-exec-1.3.jar b/lib/synthea_uscore_v3/commons-exec-1.3.jar similarity index 100% rename from lib/synthea/commons-exec-1.3.jar rename to lib/synthea_uscore_v3/commons-exec-1.3.jar diff --git a/lib/synthea/commons-io-2.6.jar b/lib/synthea_uscore_v3/commons-io-2.6.jar similarity index 100% rename from lib/synthea/commons-io-2.6.jar rename to lib/synthea_uscore_v3/commons-io-2.6.jar diff --git a/lib/synthea/commons-lang3-3.9.jar b/lib/synthea_uscore_v3/commons-lang3-3.9.jar similarity index 100% rename from lib/synthea/commons-lang3-3.9.jar rename to lib/synthea_uscore_v3/commons-lang3-3.9.jar diff --git a/lib/synthea/commons-logging-1.2.jar b/lib/synthea_uscore_v3/commons-logging-1.2.jar similarity index 100% rename from lib/synthea/commons-logging-1.2.jar rename to lib/synthea_uscore_v3/commons-logging-1.2.jar diff --git a/lib/synthea/commons-math-2.2.jar b/lib/synthea_uscore_v3/commons-math-2.2.jar similarity index 100% rename from lib/synthea/commons-math-2.2.jar rename to lib/synthea_uscore_v3/commons-math-2.2.jar diff --git a/lib/synthea/commons-math3-3.6.1.jar b/lib/synthea_uscore_v3/commons-math3-3.6.1.jar similarity index 100% rename from lib/synthea/commons-math3-3.6.1.jar rename to lib/synthea_uscore_v3/commons-math3-3.6.1.jar diff --git a/lib/synthea/commons-text-1.7.jar b/lib/synthea_uscore_v3/commons-text-1.7.jar similarity index 100% rename from lib/synthea/commons-text-1.7.jar rename to lib/synthea_uscore_v3/commons-text-1.7.jar diff --git a/lib/synthea/commons-validator-1.4.0.jar b/lib/synthea_uscore_v3/commons-validator-1.4.0.jar similarity index 100% rename from lib/synthea/commons-validator-1.4.0.jar rename to lib/synthea_uscore_v3/commons-validator-1.4.0.jar diff --git a/lib/synthea/cql-1.3.17.jar b/lib/synthea_uscore_v3/cql-1.3.17.jar similarity index 100% rename from lib/synthea/cql-1.3.17.jar rename to lib/synthea_uscore_v3/cql-1.3.17.jar diff --git a/lib/synthea/cql-engine-1.3.12.jar b/lib/synthea_uscore_v3/cql-engine-1.3.12.jar similarity index 100% rename from lib/synthea/cql-engine-1.3.12.jar rename to lib/synthea_uscore_v3/cql-engine-1.3.12.jar diff --git a/lib/synthea/cql-to-elm-1.3.17.jar b/lib/synthea_uscore_v3/cql-to-elm-1.3.17.jar similarity index 100% rename from lib/synthea/cql-to-elm-1.3.17.jar rename to lib/synthea_uscore_v3/cql-to-elm-1.3.17.jar diff --git a/lib/synthea/eclipselink-2.6.0.jar b/lib/synthea_uscore_v3/eclipselink-2.6.0.jar similarity index 100% rename from lib/synthea/eclipselink-2.6.0.jar rename to lib/synthea_uscore_v3/eclipselink-2.6.0.jar diff --git a/lib/synthea/elm-1.3.17.jar b/lib/synthea_uscore_v3/elm-1.3.17.jar similarity index 100% rename from lib/synthea/elm-1.3.17.jar rename to lib/synthea_uscore_v3/elm-1.3.17.jar diff --git a/lib/synthea/error_prone_annotations-2.3.2.jar b/lib/synthea_uscore_v3/error_prone_annotations-2.3.2.jar similarity index 100% rename from lib/synthea/error_prone_annotations-2.3.2.jar rename to lib/synthea_uscore_v3/error_prone_annotations-2.3.2.jar diff --git a/lib/synthea/failureaccess-1.0.1.jar b/lib/synthea_uscore_v3/failureaccess-1.0.1.jar similarity index 100% rename from lib/synthea/failureaccess-1.0.1.jar rename to lib/synthea_uscore_v3/failureaccess-1.0.1.jar diff --git a/lib/synthea/freemarker-2.3.26-incubating.jar b/lib/synthea_uscore_v3/freemarker-2.3.26-incubating.jar similarity index 100% rename from lib/synthea/freemarker-2.3.26-incubating.jar rename to lib/synthea_uscore_v3/freemarker-2.3.26-incubating.jar diff --git a/lib/synthea/graphviz-java-0.2.2.jar b/lib/synthea_uscore_v3/graphviz-java-0.2.2.jar similarity index 100% rename from lib/synthea/graphviz-java-0.2.2.jar rename to lib/synthea_uscore_v3/graphviz-java-0.2.2.jar diff --git a/lib/synthea/gson-2.8.5.jar b/lib/synthea_uscore_v3/gson-2.8.5.jar similarity index 100% rename from lib/synthea/gson-2.8.5.jar rename to lib/synthea_uscore_v3/gson-2.8.5.jar diff --git a/lib/synthea/guava-28.0-jre.jar b/lib/synthea_uscore_v3/guava-28.0-jre.jar similarity index 100% rename from lib/synthea/guava-28.0-jre.jar rename to lib/synthea_uscore_v3/guava-28.0-jre.jar diff --git a/lib/synthea/h2-1.4.196.jar b/lib/synthea_uscore_v3/h2-1.4.196.jar similarity index 100% rename from lib/synthea/h2-1.4.196.jar rename to lib/synthea_uscore_v3/h2-1.4.196.jar diff --git a/lib/synthea/hamcrest-all-1.3.jar b/lib/synthea_uscore_v3/hamcrest-all-1.3.jar similarity index 100% rename from lib/synthea/hamcrest-all-1.3.jar rename to lib/synthea_uscore_v3/hamcrest-all-1.3.jar diff --git a/lib/synthea/hamcrest-core-1.3.jar b/lib/synthea_uscore_v3/hamcrest-core-1.3.jar similarity index 100% rename from lib/synthea/hamcrest-core-1.3.jar rename to lib/synthea_uscore_v3/hamcrest-core-1.3.jar diff --git a/lib/synthea/hamcrest-json-0.2.jar b/lib/synthea_uscore_v3/hamcrest-json-0.2.jar similarity index 100% rename from lib/synthea/hamcrest-json-0.2.jar rename to lib/synthea_uscore_v3/hamcrest-json-0.2.jar diff --git a/lib/synthea/hapi-fhir-base-4.1.0.jar b/lib/synthea_uscore_v3/hapi-fhir-base-4.1.0.jar similarity index 100% rename from lib/synthea/hapi-fhir-base-4.1.0.jar rename to lib/synthea_uscore_v3/hapi-fhir-base-4.1.0.jar diff --git a/lib/synthea/hapi-fhir-client-4.1.0.jar b/lib/synthea_uscore_v3/hapi-fhir-client-4.1.0.jar similarity index 100% rename from lib/synthea/hapi-fhir-client-4.1.0.jar rename to lib/synthea_uscore_v3/hapi-fhir-client-4.1.0.jar diff --git a/lib/synthea/hapi-fhir-structures-dstu2-4.1.0.jar b/lib/synthea_uscore_v3/hapi-fhir-structures-dstu2-4.1.0.jar similarity index 100% rename from lib/synthea/hapi-fhir-structures-dstu2-4.1.0.jar rename to lib/synthea_uscore_v3/hapi-fhir-structures-dstu2-4.1.0.jar diff --git a/lib/synthea/hapi-fhir-structures-dstu3-4.1.0.jar b/lib/synthea_uscore_v3/hapi-fhir-structures-dstu3-4.1.0.jar similarity index 100% rename from lib/synthea/hapi-fhir-structures-dstu3-4.1.0.jar rename to lib/synthea_uscore_v3/hapi-fhir-structures-dstu3-4.1.0.jar diff --git a/lib/synthea/hapi-fhir-structures-r4-4.1.0.jar b/lib/synthea_uscore_v3/hapi-fhir-structures-r4-4.1.0.jar similarity index 100% rename from lib/synthea/hapi-fhir-structures-r4-4.1.0.jar rename to lib/synthea_uscore_v3/hapi-fhir-structures-r4-4.1.0.jar diff --git a/lib/synthea/httpclient-4.5.9.jar b/lib/synthea_uscore_v3/httpclient-4.5.9.jar similarity index 100% rename from lib/synthea/httpclient-4.5.9.jar rename to lib/synthea_uscore_v3/httpclient-4.5.9.jar diff --git a/lib/synthea/httpcore-4.4.11.jar b/lib/synthea_uscore_v3/httpcore-4.4.11.jar similarity index 100% rename from lib/synthea/httpcore-4.4.11.jar rename to lib/synthea_uscore_v3/httpcore-4.4.11.jar diff --git a/lib/synthea/istack-commons-runtime-3.0.5.jar b/lib/synthea_uscore_v3/istack-commons-runtime-3.0.5.jar similarity index 100% rename from lib/synthea/istack-commons-runtime-3.0.5.jar rename to lib/synthea_uscore_v3/istack-commons-runtime-3.0.5.jar diff --git a/lib/synthea/j2objc-annotations-1.3.jar b/lib/synthea_uscore_v3/j2objc-annotations-1.3.jar similarity index 100% rename from lib/synthea/j2objc-annotations-1.3.jar rename to lib/synthea_uscore_v3/j2objc-annotations-1.3.jar diff --git a/lib/synthea/j2v8_linux_x86_64-4.6.0.jar b/lib/synthea_uscore_v3/j2v8_linux_x86_64-4.6.0.jar similarity index 100% rename from lib/synthea/j2v8_linux_x86_64-4.6.0.jar rename to lib/synthea_uscore_v3/j2v8_linux_x86_64-4.6.0.jar diff --git a/lib/synthea/j2v8_macosx_x86_64-4.6.0.jar b/lib/synthea_uscore_v3/j2v8_macosx_x86_64-4.6.0.jar similarity index 100% rename from lib/synthea/j2v8_macosx_x86_64-4.6.0.jar rename to lib/synthea_uscore_v3/j2v8_macosx_x86_64-4.6.0.jar diff --git a/lib/synthea/j2v8_win32_x86-4.6.0.jar b/lib/synthea_uscore_v3/j2v8_win32_x86-4.6.0.jar similarity index 100% rename from lib/synthea/j2v8_win32_x86-4.6.0.jar rename to lib/synthea_uscore_v3/j2v8_win32_x86-4.6.0.jar diff --git a/lib/synthea/j2v8_win32_x86_64-4.6.0.jar b/lib/synthea_uscore_v3/j2v8_win32_x86_64-4.6.0.jar similarity index 100% rename from lib/synthea/j2v8_win32_x86_64-4.6.0.jar rename to lib/synthea_uscore_v3/j2v8_win32_x86_64-4.6.0.jar diff --git a/lib/synthea/jackson-annotations-2.10.1.jar b/lib/synthea_uscore_v3/jackson-annotations-2.10.1.jar similarity index 100% rename from lib/synthea/jackson-annotations-2.10.1.jar rename to lib/synthea_uscore_v3/jackson-annotations-2.10.1.jar diff --git a/lib/synthea/jackson-core-2.10.1.jar b/lib/synthea_uscore_v3/jackson-core-2.10.1.jar similarity index 100% rename from lib/synthea/jackson-core-2.10.1.jar rename to lib/synthea_uscore_v3/jackson-core-2.10.1.jar diff --git a/lib/synthea/jackson-databind-2.10.1.jar b/lib/synthea_uscore_v3/jackson-databind-2.10.1.jar similarity index 100% rename from lib/synthea/jackson-databind-2.10.1.jar rename to lib/synthea_uscore_v3/jackson-databind-2.10.1.jar diff --git a/lib/synthea/jackson-dataformat-csv-2.8.8.jar b/lib/synthea_uscore_v3/jackson-dataformat-csv-2.8.8.jar similarity index 100% rename from lib/synthea/jackson-dataformat-csv-2.8.8.jar rename to lib/synthea_uscore_v3/jackson-dataformat-csv-2.8.8.jar diff --git a/lib/synthea/javaparser-1.0.11.jar b/lib/synthea_uscore_v3/javaparser-1.0.11.jar similarity index 100% rename from lib/synthea/javaparser-1.0.11.jar rename to lib/synthea_uscore_v3/javaparser-1.0.11.jar diff --git a/lib/synthea/javax.activation-1.2.0.jar b/lib/synthea_uscore_v3/javax.activation-1.2.0.jar similarity index 100% rename from lib/synthea/javax.activation-1.2.0.jar rename to lib/synthea_uscore_v3/javax.activation-1.2.0.jar diff --git a/lib/synthea/javax.json-1.0.4.jar b/lib/synthea_uscore_v3/javax.json-1.0.4.jar similarity index 100% rename from lib/synthea/javax.json-1.0.4.jar rename to lib/synthea_uscore_v3/javax.json-1.0.4.jar diff --git a/lib/synthea/javax.persistence-2.1.0.jar b/lib/synthea_uscore_v3/javax.persistence-2.1.0.jar similarity index 100% rename from lib/synthea/javax.persistence-2.1.0.jar rename to lib/synthea_uscore_v3/javax.persistence-2.1.0.jar diff --git a/lib/synthea/jaxb-api-2.3.0.jar b/lib/synthea_uscore_v3/jaxb-api-2.3.0.jar similarity index 100% rename from lib/synthea/jaxb-api-2.3.0.jar rename to lib/synthea_uscore_v3/jaxb-api-2.3.0.jar diff --git a/lib/synthea/jaxb-core-2.3.0.1.jar b/lib/synthea_uscore_v3/jaxb-core-2.3.0.1.jar similarity index 100% rename from lib/synthea/jaxb-core-2.3.0.1.jar rename to lib/synthea_uscore_v3/jaxb-core-2.3.0.1.jar diff --git a/lib/synthea/jaxb-core-2.3.0.jar b/lib/synthea_uscore_v3/jaxb-core-2.3.0.jar similarity index 100% rename from lib/synthea/jaxb-core-2.3.0.jar rename to lib/synthea_uscore_v3/jaxb-core-2.3.0.jar diff --git a/lib/synthea/jaxb-impl-2.3.0.1.jar b/lib/synthea_uscore_v3/jaxb-impl-2.3.0.1.jar similarity index 100% rename from lib/synthea/jaxb-impl-2.3.0.1.jar rename to lib/synthea_uscore_v3/jaxb-impl-2.3.0.1.jar diff --git a/lib/synthea/jaxb-runtime-2.3.0.jar b/lib/synthea_uscore_v3/jaxb-runtime-2.3.0.jar similarity index 100% rename from lib/synthea/jaxb-runtime-2.3.0.jar rename to lib/synthea_uscore_v3/jaxb-runtime-2.3.0.jar diff --git a/lib/synthea/jaxb2-basics-0.12.0.jar b/lib/synthea_uscore_v3/jaxb2-basics-0.12.0.jar similarity index 100% rename from lib/synthea/jaxb2-basics-0.12.0.jar rename to lib/synthea_uscore_v3/jaxb2-basics-0.12.0.jar diff --git a/lib/synthea/jaxb2-basics-runtime-0.12.0.jar b/lib/synthea_uscore_v3/jaxb2-basics-runtime-0.12.0.jar similarity index 100% rename from lib/synthea/jaxb2-basics-runtime-0.12.0.jar rename to lib/synthea_uscore_v3/jaxb2-basics-runtime-0.12.0.jar diff --git a/lib/synthea/jaxb2-basics-tools-0.12.0.jar b/lib/synthea_uscore_v3/jaxb2-basics-tools-0.12.0.jar similarity index 100% rename from lib/synthea/jaxb2-basics-tools-0.12.0.jar rename to lib/synthea_uscore_v3/jaxb2-basics-tools-0.12.0.jar diff --git a/lib/synthea/jcl-over-slf4j-1.7.28.jar b/lib/synthea_uscore_v3/jcl-over-slf4j-1.7.28.jar similarity index 100% rename from lib/synthea/jcl-over-slf4j-1.7.28.jar rename to lib/synthea_uscore_v3/jcl-over-slf4j-1.7.28.jar diff --git a/lib/synthea/jfreechart-1.5.0.jar b/lib/synthea_uscore_v3/jfreechart-1.5.0.jar similarity index 100% rename from lib/synthea/jfreechart-1.5.0.jar rename to lib/synthea_uscore_v3/jfreechart-1.5.0.jar diff --git a/lib/synthea/jigsaw-2.2.6.jar b/lib/synthea_uscore_v3/jigsaw-2.2.6.jar similarity index 100% rename from lib/synthea/jigsaw-2.2.6.jar rename to lib/synthea_uscore_v3/jigsaw-2.2.6.jar diff --git a/lib/synthea/jopt-simple-4.7.jar b/lib/synthea_uscore_v3/jopt-simple-4.7.jar similarity index 100% rename from lib/synthea/jopt-simple-4.7.jar rename to lib/synthea_uscore_v3/jopt-simple-4.7.jar diff --git a/lib/synthea/jsbml-1.4.jar b/lib/synthea_uscore_v3/jsbml-1.4.jar similarity index 100% rename from lib/synthea/jsbml-1.4.jar rename to lib/synthea_uscore_v3/jsbml-1.4.jar diff --git a/lib/synthea/jsbml-arrays-1.4.jar b/lib/synthea_uscore_v3/jsbml-arrays-1.4.jar similarity index 100% rename from lib/synthea/jsbml-arrays-1.4.jar rename to lib/synthea_uscore_v3/jsbml-arrays-1.4.jar diff --git a/lib/synthea/jsbml-comp-1.4.jar b/lib/synthea_uscore_v3/jsbml-comp-1.4.jar similarity index 100% rename from lib/synthea/jsbml-comp-1.4.jar rename to lib/synthea_uscore_v3/jsbml-comp-1.4.jar diff --git a/lib/synthea/jsbml-core-1.4.jar b/lib/synthea_uscore_v3/jsbml-core-1.4.jar similarity index 100% rename from lib/synthea/jsbml-core-1.4.jar rename to lib/synthea_uscore_v3/jsbml-core-1.4.jar diff --git a/lib/synthea/jsbml-distrib-1.3.1.jar b/lib/synthea_uscore_v3/jsbml-distrib-1.3.1.jar similarity index 100% rename from lib/synthea/jsbml-distrib-1.3.1.jar rename to lib/synthea_uscore_v3/jsbml-distrib-1.3.1.jar diff --git a/lib/synthea/jsbml-dyn-1.4.jar b/lib/synthea_uscore_v3/jsbml-dyn-1.4.jar similarity index 100% rename from lib/synthea/jsbml-dyn-1.4.jar rename to lib/synthea_uscore_v3/jsbml-dyn-1.4.jar diff --git a/lib/synthea/jsbml-fbc-1.4.jar b/lib/synthea_uscore_v3/jsbml-fbc-1.4.jar similarity index 100% rename from lib/synthea/jsbml-fbc-1.4.jar rename to lib/synthea_uscore_v3/jsbml-fbc-1.4.jar diff --git a/lib/synthea/jsbml-groups-1.4.jar b/lib/synthea_uscore_v3/jsbml-groups-1.4.jar similarity index 100% rename from lib/synthea/jsbml-groups-1.4.jar rename to lib/synthea_uscore_v3/jsbml-groups-1.4.jar diff --git a/lib/synthea/jsbml-layout-1.4.jar b/lib/synthea_uscore_v3/jsbml-layout-1.4.jar similarity index 100% rename from lib/synthea/jsbml-layout-1.4.jar rename to lib/synthea_uscore_v3/jsbml-layout-1.4.jar diff --git a/lib/synthea/jsbml-multi-1.4.jar b/lib/synthea_uscore_v3/jsbml-multi-1.4.jar similarity index 100% rename from lib/synthea/jsbml-multi-1.4.jar rename to lib/synthea_uscore_v3/jsbml-multi-1.4.jar diff --git a/lib/synthea/jsbml-qual-1.4.jar b/lib/synthea_uscore_v3/jsbml-qual-1.4.jar similarity index 100% rename from lib/synthea/jsbml-qual-1.4.jar rename to lib/synthea_uscore_v3/jsbml-qual-1.4.jar diff --git a/lib/synthea/jsbml-render-1.4.jar b/lib/synthea_uscore_v3/jsbml-render-1.4.jar similarity index 100% rename from lib/synthea/jsbml-render-1.4.jar rename to lib/synthea_uscore_v3/jsbml-render-1.4.jar diff --git a/lib/synthea/jsbml-req-1.4.jar b/lib/synthea_uscore_v3/jsbml-req-1.4.jar similarity index 100% rename from lib/synthea/jsbml-req-1.4.jar rename to lib/synthea_uscore_v3/jsbml-req-1.4.jar diff --git a/lib/synthea/jsbml-spatial-1.4.jar b/lib/synthea_uscore_v3/jsbml-spatial-1.4.jar similarity index 100% rename from lib/synthea/jsbml-spatial-1.4.jar rename to lib/synthea_uscore_v3/jsbml-spatial-1.4.jar diff --git a/lib/synthea/jsbml-tidy-1.4.jar b/lib/synthea_uscore_v3/jsbml-tidy-1.4.jar similarity index 100% rename from lib/synthea/jsbml-tidy-1.4.jar rename to lib/synthea_uscore_v3/jsbml-tidy-1.4.jar diff --git a/lib/synthea/json-20090211.jar b/lib/synthea_uscore_v3/json-20090211.jar similarity index 100% rename from lib/synthea/json-20090211.jar rename to lib/synthea_uscore_v3/json-20090211.jar diff --git a/lib/synthea/json-path-2.4.0.jar b/lib/synthea_uscore_v3/json-path-2.4.0.jar similarity index 100% rename from lib/synthea/json-path-2.4.0.jar rename to lib/synthea_uscore_v3/json-path-2.4.0.jar diff --git a/lib/synthea/json-simple-1.1.1.jar b/lib/synthea_uscore_v3/json-simple-1.1.1.jar similarity index 100% rename from lib/synthea/json-simple-1.1.1.jar rename to lib/synthea_uscore_v3/json-simple-1.1.1.jar diff --git a/lib/synthea/json-smart-2.3.jar b/lib/synthea_uscore_v3/json-smart-2.3.jar similarity index 100% rename from lib/synthea/json-smart-2.3.jar rename to lib/synthea_uscore_v3/json-smart-2.3.jar diff --git a/lib/synthea/jsonassert-1.1.1.jar b/lib/synthea_uscore_v3/jsonassert-1.1.1.jar similarity index 100% rename from lib/synthea/jsonassert-1.1.1.jar rename to lib/synthea_uscore_v3/jsonassert-1.1.1.jar diff --git a/lib/synthea/jsr305-3.0.2.jar b/lib/synthea_uscore_v3/jsr305-3.0.2.jar similarity index 100% rename from lib/synthea/jsr305-3.0.2.jar rename to lib/synthea_uscore_v3/jsr305-3.0.2.jar diff --git a/lib/synthea/jtidy-r938.jar b/lib/synthea_uscore_v3/jtidy-r938.jar similarity index 100% rename from lib/synthea/jtidy-r938.jar rename to lib/synthea_uscore_v3/jtidy-r938.jar diff --git a/lib/synthea/jul-to-slf4j-1.7.25.jar b/lib/synthea_uscore_v3/jul-to-slf4j-1.7.25.jar similarity index 100% rename from lib/synthea/jul-to-slf4j-1.7.25.jar rename to lib/synthea_uscore_v3/jul-to-slf4j-1.7.25.jar diff --git a/lib/synthea/junit-4.12.jar b/lib/synthea_uscore_v3/junit-4.12.jar similarity index 100% rename from lib/synthea/junit-4.12.jar rename to lib/synthea_uscore_v3/junit-4.12.jar diff --git a/lib/synthea/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar b/lib/synthea_uscore_v3/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar similarity index 100% rename from lib/synthea/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar rename to lib/synthea_uscore_v3/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar diff --git a/lib/synthea/log4j-1.2-api-2.3.jar b/lib/synthea_uscore_v3/log4j-1.2-api-2.3.jar similarity index 100% rename from lib/synthea/log4j-1.2-api-2.3.jar rename to lib/synthea_uscore_v3/log4j-1.2-api-2.3.jar diff --git a/lib/synthea/log4j-1.2.17.jar b/lib/synthea_uscore_v3/log4j-1.2.17.jar similarity index 100% rename from lib/synthea/log4j-1.2.17.jar rename to lib/synthea_uscore_v3/log4j-1.2.17.jar diff --git a/lib/synthea/log4j-api-2.3.jar b/lib/synthea_uscore_v3/log4j-api-2.3.jar similarity index 100% rename from lib/synthea/log4j-api-2.3.jar rename to lib/synthea_uscore_v3/log4j-api-2.3.jar diff --git a/lib/synthea/log4j-core-2.3.jar b/lib/synthea_uscore_v3/log4j-core-2.3.jar similarity index 100% rename from lib/synthea/log4j-core-2.3.jar rename to lib/synthea_uscore_v3/log4j-core-2.3.jar diff --git a/lib/synthea/model-1.3.17.jar b/lib/synthea_uscore_v3/model-1.3.17.jar similarity index 100% rename from lib/synthea/model-1.3.17.jar rename to lib/synthea_uscore_v3/model-1.3.17.jar diff --git a/lib/synthea/org.abego.treelayout.core-1.0.1.jar b/lib/synthea_uscore_v3/org.abego.treelayout.core-1.0.1.jar similarity index 100% rename from lib/synthea/org.abego.treelayout.core-1.0.1.jar rename to lib/synthea_uscore_v3/org.abego.treelayout.core-1.0.1.jar diff --git a/lib/synthea/org.hl7.fhir.dstu3-4.1.0.jar b/lib/synthea_uscore_v3/org.hl7.fhir.dstu3-4.1.0.jar similarity index 100% rename from lib/synthea/org.hl7.fhir.dstu3-4.1.0.jar rename to lib/synthea_uscore_v3/org.hl7.fhir.dstu3-4.1.0.jar diff --git a/lib/synthea/org.hl7.fhir.r4-4.1.0.jar b/lib/synthea_uscore_v3/org.hl7.fhir.r4-4.1.0.jar similarity index 100% rename from lib/synthea/org.hl7.fhir.r4-4.1.0.jar rename to lib/synthea_uscore_v3/org.hl7.fhir.r4-4.1.0.jar diff --git a/lib/synthea/org.hl7.fhir.utilities-4.1.0.jar b/lib/synthea_uscore_v3/org.hl7.fhir.utilities-4.1.0.jar similarity index 100% rename from lib/synthea/org.hl7.fhir.utilities-4.1.0.jar rename to lib/synthea_uscore_v3/org.hl7.fhir.utilities-4.1.0.jar diff --git a/lib/synthea/qdm-1.3.17.jar b/lib/synthea_uscore_v3/qdm-1.3.17.jar similarity index 100% rename from lib/synthea/qdm-1.3.17.jar rename to lib/synthea_uscore_v3/qdm-1.3.17.jar diff --git a/lib/synthea/quick-1.3.17.jar b/lib/synthea_uscore_v3/quick-1.3.17.jar similarity index 100% rename from lib/synthea/quick-1.3.17.jar rename to lib/synthea_uscore_v3/quick-1.3.17.jar diff --git a/lib/synthea/serializer-2.7.2.jar b/lib/synthea_uscore_v3/serializer-2.7.2.jar similarity index 100% rename from lib/synthea/serializer-2.7.2.jar rename to lib/synthea_uscore_v3/serializer-2.7.2.jar diff --git a/lib/synthea/slf4j-api-1.7.28.jar b/lib/synthea_uscore_v3/slf4j-api-1.7.28.jar similarity index 100% rename from lib/synthea/slf4j-api-1.7.28.jar rename to lib/synthea_uscore_v3/slf4j-api-1.7.28.jar diff --git a/lib/synthea/slf4j-log4j12-1.7.25.jar b/lib/synthea_uscore_v3/slf4j-log4j12-1.7.25.jar similarity index 100% rename from lib/synthea/slf4j-log4j12-1.7.25.jar rename to lib/synthea_uscore_v3/slf4j-log4j12-1.7.25.jar diff --git a/lib/synthea/snakeyaml-1.25.jar b/lib/synthea_uscore_v3/snakeyaml-1.25.jar similarity index 100% rename from lib/synthea/snakeyaml-1.25.jar rename to lib/synthea_uscore_v3/snakeyaml-1.25.jar diff --git a/lib/synthea/spring-beans-5.2.7.RELEASE.jar b/lib/synthea_uscore_v3/spring-beans-5.2.7.RELEASE.jar similarity index 100% rename from lib/synthea/spring-beans-5.2.7.RELEASE.jar rename to lib/synthea_uscore_v3/spring-beans-5.2.7.RELEASE.jar diff --git a/lib/synthea/spring-core-5.2.7.RELEASE.jar b/lib/synthea_uscore_v3/spring-core-5.2.7.RELEASE.jar similarity index 100% rename from lib/synthea/spring-core-5.2.7.RELEASE.jar rename to lib/synthea_uscore_v3/spring-core-5.2.7.RELEASE.jar diff --git a/lib/synthea/spring-jcl-5.2.7.RELEASE.jar b/lib/synthea_uscore_v3/spring-jcl-5.2.7.RELEASE.jar similarity index 100% rename from lib/synthea/spring-jcl-5.2.7.RELEASE.jar rename to lib/synthea_uscore_v3/spring-jcl-5.2.7.RELEASE.jar diff --git a/lib/synthea/spring-web-5.2.7.RELEASE.jar b/lib/synthea_uscore_v3/spring-web-5.2.7.RELEASE.jar similarity index 100% rename from lib/synthea/spring-web-5.2.7.RELEASE.jar rename to lib/synthea_uscore_v3/spring-web-5.2.7.RELEASE.jar diff --git a/lib/synthea/stax-ex-1.7.8.jar b/lib/synthea_uscore_v3/stax-ex-1.7.8.jar similarity index 100% rename from lib/synthea/stax-ex-1.7.8.jar rename to lib/synthea_uscore_v3/stax-ex-1.7.8.jar diff --git a/lib/synthea/stax2-api-3.1.4.jar b/lib/synthea_uscore_v3/stax2-api-3.1.4.jar similarity index 100% rename from lib/synthea/stax2-api-3.1.4.jar rename to lib/synthea_uscore_v3/stax2-api-3.1.4.jar diff --git a/lib/synthea/staxmate-2.3.0.jar b/lib/synthea_uscore_v3/staxmate-2.3.0.jar similarity index 100% rename from lib/synthea/staxmate-2.3.0.jar rename to lib/synthea_uscore_v3/staxmate-2.3.0.jar diff --git a/lib/synthea/synthea.jar b/lib/synthea_uscore_v3/synthea.jar similarity index 97% rename from lib/synthea/synthea.jar rename to lib/synthea_uscore_v3/synthea.jar index 2664a89..2abf311 100644 Binary files a/lib/synthea/synthea.jar and b/lib/synthea_uscore_v3/synthea.jar differ diff --git a/lib/synthea/txw2-2.3.0.jar b/lib/synthea_uscore_v3/txw2-2.3.0.jar similarity index 100% rename from lib/synthea/txw2-2.3.0.jar rename to lib/synthea_uscore_v3/txw2-2.3.0.jar diff --git a/lib/synthea/ucum-1.0.2.jar b/lib/synthea_uscore_v3/ucum-1.0.2.jar similarity index 100% rename from lib/synthea/ucum-1.0.2.jar rename to lib/synthea_uscore_v3/ucum-1.0.2.jar diff --git a/lib/synthea/validation-api-1.1.0.Final.jar b/lib/synthea_uscore_v3/validation-api-1.1.0.Final.jar similarity index 100% rename from lib/synthea/validation-api-1.1.0.Final.jar rename to lib/synthea_uscore_v3/validation-api-1.1.0.Final.jar diff --git a/lib/synthea/woodstox-core-5.0.1.jar b/lib/synthea_uscore_v3/woodstox-core-5.0.1.jar similarity index 100% rename from lib/synthea/woodstox-core-5.0.1.jar rename to lib/synthea_uscore_v3/woodstox-core-5.0.1.jar diff --git a/lib/synthea/xalan-2.7.2.jar b/lib/synthea_uscore_v3/xalan-2.7.2.jar similarity index 100% rename from lib/synthea/xalan-2.7.2.jar rename to lib/synthea_uscore_v3/xalan-2.7.2.jar diff --git a/lib/synthea/xml-apis-1.3.04.jar b/lib/synthea_uscore_v3/xml-apis-1.3.04.jar similarity index 100% rename from lib/synthea/xml-apis-1.3.04.jar rename to lib/synthea_uscore_v3/xml-apis-1.3.04.jar diff --git a/lib/synthea/xml-apis-ext-1.3.04.jar b/lib/synthea_uscore_v3/xml-apis-ext-1.3.04.jar similarity index 100% rename from lib/synthea/xml-apis-ext-1.3.04.jar rename to lib/synthea_uscore_v3/xml-apis-ext-1.3.04.jar diff --git a/lib/synthea/xmlgraphics-commons-2.2.jar b/lib/synthea_uscore_v3/xmlgraphics-commons-2.2.jar similarity index 100% rename from lib/synthea/xmlgraphics-commons-2.2.jar rename to lib/synthea_uscore_v3/xmlgraphics-commons-2.2.jar diff --git a/lib/synthea/xpp3-1.1.4c.jar b/lib/synthea_uscore_v3/xpp3-1.1.4c.jar similarity index 100% rename from lib/synthea/xpp3-1.1.4c.jar rename to lib/synthea_uscore_v3/xpp3-1.1.4c.jar diff --git a/lib/synthea/xpp3_min-1.1.4c.jar b/lib/synthea_uscore_v3/xpp3_min-1.1.4c.jar similarity index 100% rename from lib/synthea/xpp3_min-1.1.4c.jar rename to lib/synthea_uscore_v3/xpp3_min-1.1.4c.jar diff --git a/lib/synthea/xpp3_xpath-1.1.4c.jar b/lib/synthea_uscore_v3/xpp3_xpath-1.1.4c.jar similarity index 100% rename from lib/synthea/xpp3_xpath-1.1.4c.jar rename to lib/synthea_uscore_v3/xpp3_xpath-1.1.4c.jar diff --git a/lib/synthea/xstream-1.3.1.jar b/lib/synthea_uscore_v3/xstream-1.3.1.jar similarity index 100% rename from lib/synthea/xstream-1.3.1.jar rename to lib/synthea_uscore_v3/xstream-1.3.1.jar diff --git a/lib/synthea_uscore_v4/FastInfoset-1.2.16.jar b/lib/synthea_uscore_v4/FastInfoset-1.2.16.jar new file mode 100644 index 0000000..6e91f1c Binary files /dev/null and b/lib/synthea_uscore_v4/FastInfoset-1.2.16.jar differ diff --git a/lib/synthea_uscore_v4/ST4-4.0.8.jar b/lib/synthea_uscore_v4/ST4-4.0.8.jar new file mode 100644 index 0000000..144828b Binary files /dev/null and b/lib/synthea_uscore_v4/ST4-4.0.8.jar differ diff --git a/lib/synthea_uscore_v4/SimulationCoreLibrary_v1.5_slim.jar b/lib/synthea_uscore_v4/SimulationCoreLibrary_v1.5_slim.jar new file mode 100755 index 0000000..cbac528 Binary files /dev/null and b/lib/synthea_uscore_v4/SimulationCoreLibrary_v1.5_slim.jar differ diff --git a/lib/synthea_uscore_v4/accessors-smart-1.2.jar b/lib/synthea_uscore_v4/accessors-smart-1.2.jar new file mode 100644 index 0000000..f4505e9 Binary files /dev/null and b/lib/synthea_uscore_v4/accessors-smart-1.2.jar differ diff --git a/lib/synthea_uscore_v4/antlr-runtime-3.5.2.jar b/lib/synthea_uscore_v4/antlr-runtime-3.5.2.jar new file mode 100644 index 0000000..d48e3e8 Binary files /dev/null and b/lib/synthea_uscore_v4/antlr-runtime-3.5.2.jar differ diff --git a/lib/synthea_uscore_v4/antlr4-4.5.jar b/lib/synthea_uscore_v4/antlr4-4.5.jar new file mode 100644 index 0000000..a20a536 Binary files /dev/null and b/lib/synthea_uscore_v4/antlr4-4.5.jar differ diff --git a/lib/synthea_uscore_v4/antlr4-runtime-4.5.jar b/lib/synthea_uscore_v4/antlr4-runtime-4.5.jar new file mode 100644 index 0000000..924fcce Binary files /dev/null and b/lib/synthea_uscore_v4/antlr4-runtime-4.5.jar differ diff --git a/lib/synthea_uscore_v4/asm-5.0.4.jar b/lib/synthea_uscore_v4/asm-5.0.4.jar new file mode 100644 index 0000000..cdb283d Binary files /dev/null and b/lib/synthea_uscore_v4/asm-5.0.4.jar differ diff --git a/lib/synthea_uscore_v4/batik-anim-1.9.jar b/lib/synthea_uscore_v4/batik-anim-1.9.jar new file mode 100644 index 0000000..354c8a8 Binary files /dev/null and b/lib/synthea_uscore_v4/batik-anim-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-awt-util-1.9.jar b/lib/synthea_uscore_v4/batik-awt-util-1.9.jar new file mode 100644 index 0000000..dd1c928 Binary files /dev/null and b/lib/synthea_uscore_v4/batik-awt-util-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-bridge-1.9.jar b/lib/synthea_uscore_v4/batik-bridge-1.9.jar new file mode 100644 index 0000000..5f9aa7e Binary files /dev/null and b/lib/synthea_uscore_v4/batik-bridge-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-codec-1.9.jar b/lib/synthea_uscore_v4/batik-codec-1.9.jar new file mode 100644 index 0000000..48074c2 Binary files /dev/null and b/lib/synthea_uscore_v4/batik-codec-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-constants-1.9.jar b/lib/synthea_uscore_v4/batik-constants-1.9.jar new file mode 100644 index 0000000..ebb46b4 Binary files /dev/null and b/lib/synthea_uscore_v4/batik-constants-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-css-1.9.jar b/lib/synthea_uscore_v4/batik-css-1.9.jar new file mode 100644 index 0000000..7e22041 Binary files /dev/null and b/lib/synthea_uscore_v4/batik-css-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-dom-1.9.jar b/lib/synthea_uscore_v4/batik-dom-1.9.jar new file mode 100644 index 0000000..57dc6d5 Binary files /dev/null and b/lib/synthea_uscore_v4/batik-dom-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-ext-1.9.jar b/lib/synthea_uscore_v4/batik-ext-1.9.jar new file mode 100644 index 0000000..4383a50 Binary files /dev/null and b/lib/synthea_uscore_v4/batik-ext-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-gvt-1.9.jar b/lib/synthea_uscore_v4/batik-gvt-1.9.jar new file mode 100644 index 0000000..6af8fbe Binary files /dev/null and b/lib/synthea_uscore_v4/batik-gvt-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-i18n-1.9.jar b/lib/synthea_uscore_v4/batik-i18n-1.9.jar new file mode 100644 index 0000000..109964b Binary files /dev/null and b/lib/synthea_uscore_v4/batik-i18n-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-parser-1.9.jar b/lib/synthea_uscore_v4/batik-parser-1.9.jar new file mode 100644 index 0000000..c9fce6f Binary files /dev/null and b/lib/synthea_uscore_v4/batik-parser-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-rasterizer-1.9.jar b/lib/synthea_uscore_v4/batik-rasterizer-1.9.jar new file mode 100644 index 0000000..a517b19 Binary files /dev/null and b/lib/synthea_uscore_v4/batik-rasterizer-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-script-1.9.jar b/lib/synthea_uscore_v4/batik-script-1.9.jar new file mode 100644 index 0000000..9867fd7 Binary files /dev/null and b/lib/synthea_uscore_v4/batik-script-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-svg-dom-1.9.jar b/lib/synthea_uscore_v4/batik-svg-dom-1.9.jar new file mode 100644 index 0000000..91bd00d Binary files /dev/null and b/lib/synthea_uscore_v4/batik-svg-dom-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-svggen-1.9.jar b/lib/synthea_uscore_v4/batik-svggen-1.9.jar new file mode 100644 index 0000000..fdac11d Binary files /dev/null and b/lib/synthea_uscore_v4/batik-svggen-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-svgrasterizer-1.9.jar b/lib/synthea_uscore_v4/batik-svgrasterizer-1.9.jar new file mode 100644 index 0000000..149ea8b Binary files /dev/null and b/lib/synthea_uscore_v4/batik-svgrasterizer-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-transcoder-1.9.jar b/lib/synthea_uscore_v4/batik-transcoder-1.9.jar new file mode 100644 index 0000000..ea6794c Binary files /dev/null and b/lib/synthea_uscore_v4/batik-transcoder-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-util-1.9.jar b/lib/synthea_uscore_v4/batik-util-1.9.jar new file mode 100644 index 0000000..382b105 Binary files /dev/null and b/lib/synthea_uscore_v4/batik-util-1.9.jar differ diff --git a/lib/synthea_uscore_v4/batik-xml-1.9.jar b/lib/synthea_uscore_v4/batik-xml-1.9.jar new file mode 100644 index 0000000..b9d5096 Binary files /dev/null and b/lib/synthea_uscore_v4/batik-xml-1.9.jar differ diff --git a/lib/synthea_uscore_v4/biojava-ontology-4.0.0.jar b/lib/synthea_uscore_v4/biojava-ontology-4.0.0.jar new file mode 100644 index 0000000..18c5001 Binary files /dev/null and b/lib/synthea_uscore_v4/biojava-ontology-4.0.0.jar differ diff --git a/lib/synthea_uscore_v4/checker-qual-3.12.0.jar b/lib/synthea_uscore_v4/checker-qual-3.12.0.jar new file mode 100644 index 0000000..e9eed80 Binary files /dev/null and b/lib/synthea_uscore_v4/checker-qual-3.12.0.jar differ diff --git a/lib/synthea_uscore_v4/commonj.sdo-2.1.1.jar b/lib/synthea_uscore_v4/commonj.sdo-2.1.1.jar new file mode 100644 index 0000000..7c85ca1 Binary files /dev/null and b/lib/synthea_uscore_v4/commonj.sdo-2.1.1.jar differ diff --git a/lib/synthea_uscore_v4/commons-beanutils-1.9.3.jar b/lib/synthea_uscore_v4/commons-beanutils-1.9.3.jar new file mode 100644 index 0000000..6728154 Binary files /dev/null and b/lib/synthea_uscore_v4/commons-beanutils-1.9.3.jar differ diff --git a/lib/synthea_uscore_v4/commons-codec-1.15.jar b/lib/synthea_uscore_v4/commons-codec-1.15.jar new file mode 100644 index 0000000..f14985a Binary files /dev/null and b/lib/synthea_uscore_v4/commons-codec-1.15.jar differ diff --git a/lib/synthea_uscore_v4/commons-collections-3.2.2.jar b/lib/synthea_uscore_v4/commons-collections-3.2.2.jar new file mode 100644 index 0000000..fa5df82 Binary files /dev/null and b/lib/synthea_uscore_v4/commons-collections-3.2.2.jar differ diff --git a/lib/synthea_uscore_v4/commons-csv-1.5.jar b/lib/synthea_uscore_v4/commons-csv-1.5.jar new file mode 100644 index 0000000..eb4775e Binary files /dev/null and b/lib/synthea_uscore_v4/commons-csv-1.5.jar differ diff --git a/lib/synthea_uscore_v4/commons-digester-1.8.jar b/lib/synthea_uscore_v4/commons-digester-1.8.jar new file mode 100644 index 0000000..1110f0a Binary files /dev/null and b/lib/synthea_uscore_v4/commons-digester-1.8.jar differ diff --git a/lib/synthea_uscore_v4/commons-exec-1.3.jar b/lib/synthea_uscore_v4/commons-exec-1.3.jar new file mode 100644 index 0000000..9a64351 Binary files /dev/null and b/lib/synthea_uscore_v4/commons-exec-1.3.jar differ diff --git a/lib/synthea_uscore_v4/commons-io-2.11.0.jar b/lib/synthea_uscore_v4/commons-io-2.11.0.jar new file mode 100644 index 0000000..be507d9 Binary files /dev/null and b/lib/synthea_uscore_v4/commons-io-2.11.0.jar differ diff --git a/lib/synthea_uscore_v4/commons-lang3-3.12.0.jar b/lib/synthea_uscore_v4/commons-lang3-3.12.0.jar new file mode 100644 index 0000000..4d434a2 Binary files /dev/null and b/lib/synthea_uscore_v4/commons-lang3-3.12.0.jar differ diff --git a/lib/synthea_uscore_v4/commons-logging-1.2.jar b/lib/synthea_uscore_v4/commons-logging-1.2.jar new file mode 100644 index 0000000..93a3b9f Binary files /dev/null and b/lib/synthea_uscore_v4/commons-logging-1.2.jar differ diff --git a/lib/synthea_uscore_v4/commons-math-2.2.jar b/lib/synthea_uscore_v4/commons-math-2.2.jar new file mode 100644 index 0000000..b29a39c Binary files /dev/null and b/lib/synthea_uscore_v4/commons-math-2.2.jar differ diff --git a/lib/synthea_uscore_v4/commons-math3-3.6.1.jar b/lib/synthea_uscore_v4/commons-math3-3.6.1.jar new file mode 100644 index 0000000..0ff582c Binary files /dev/null and b/lib/synthea_uscore_v4/commons-math3-3.6.1.jar differ diff --git a/lib/synthea_uscore_v4/commons-text-1.9.jar b/lib/synthea_uscore_v4/commons-text-1.9.jar new file mode 100644 index 0000000..cc0c690 Binary files /dev/null and b/lib/synthea_uscore_v4/commons-text-1.9.jar differ diff --git a/lib/synthea_uscore_v4/commons-validator-1.4.0.jar b/lib/synthea_uscore_v4/commons-validator-1.4.0.jar new file mode 100644 index 0000000..ddb78e7 Binary files /dev/null and b/lib/synthea_uscore_v4/commons-validator-1.4.0.jar differ diff --git a/lib/synthea_uscore_v4/cql-1.3.17.jar b/lib/synthea_uscore_v4/cql-1.3.17.jar new file mode 100644 index 0000000..6183381 Binary files /dev/null and b/lib/synthea_uscore_v4/cql-1.3.17.jar differ diff --git a/lib/synthea_uscore_v4/cql-engine-1.3.12.jar b/lib/synthea_uscore_v4/cql-engine-1.3.12.jar new file mode 100644 index 0000000..874ad34 Binary files /dev/null and b/lib/synthea_uscore_v4/cql-engine-1.3.12.jar differ diff --git a/lib/synthea_uscore_v4/cql-to-elm-1.3.17.jar b/lib/synthea_uscore_v4/cql-to-elm-1.3.17.jar new file mode 100644 index 0000000..e5c9f54 Binary files /dev/null and b/lib/synthea_uscore_v4/cql-to-elm-1.3.17.jar differ diff --git a/lib/synthea_uscore_v4/eclipselink-2.6.0.jar b/lib/synthea_uscore_v4/eclipselink-2.6.0.jar new file mode 100644 index 0000000..72b1480 Binary files /dev/null and b/lib/synthea_uscore_v4/eclipselink-2.6.0.jar differ diff --git a/lib/synthea_uscore_v4/elm-1.3.17.jar b/lib/synthea_uscore_v4/elm-1.3.17.jar new file mode 100644 index 0000000..1950d60 Binary files /dev/null and b/lib/synthea_uscore_v4/elm-1.3.17.jar differ diff --git a/lib/synthea_uscore_v4/error_prone_annotations-2.7.1.jar b/lib/synthea_uscore_v4/error_prone_annotations-2.7.1.jar new file mode 100644 index 0000000..8ea5377 Binary files /dev/null and b/lib/synthea_uscore_v4/error_prone_annotations-2.7.1.jar differ diff --git a/lib/synthea_uscore_v4/failureaccess-1.0.1.jar b/lib/synthea_uscore_v4/failureaccess-1.0.1.jar new file mode 100644 index 0000000..9b56dc7 Binary files /dev/null and b/lib/synthea_uscore_v4/failureaccess-1.0.1.jar differ diff --git a/lib/synthea_uscore_v4/freemarker-2.3.26-incubating.jar b/lib/synthea_uscore_v4/freemarker-2.3.26-incubating.jar new file mode 100644 index 0000000..9f70a14 Binary files /dev/null and b/lib/synthea_uscore_v4/freemarker-2.3.26-incubating.jar differ diff --git a/lib/synthea_uscore_v4/graphviz-java-0.2.4.jar b/lib/synthea_uscore_v4/graphviz-java-0.2.4.jar new file mode 100644 index 0000000..c408168 Binary files /dev/null and b/lib/synthea_uscore_v4/graphviz-java-0.2.4.jar differ diff --git a/lib/synthea_uscore_v4/gson-2.8.7.jar b/lib/synthea_uscore_v4/gson-2.8.7.jar new file mode 100644 index 0000000..215e823 Binary files /dev/null and b/lib/synthea_uscore_v4/gson-2.8.7.jar differ diff --git a/lib/synthea_uscore_v4/guava-31.0.1-jre.jar b/lib/synthea_uscore_v4/guava-31.0.1-jre.jar new file mode 100644 index 0000000..324887d Binary files /dev/null and b/lib/synthea_uscore_v4/guava-31.0.1-jre.jar differ diff --git a/lib/synthea_uscore_v4/hamcrest-all-1.3.jar b/lib/synthea_uscore_v4/hamcrest-all-1.3.jar new file mode 100644 index 0000000..6f62ba0 Binary files /dev/null and b/lib/synthea_uscore_v4/hamcrest-all-1.3.jar differ diff --git a/lib/synthea_uscore_v4/hamcrest-core-1.3.jar b/lib/synthea_uscore_v4/hamcrest-core-1.3.jar new file mode 100644 index 0000000..9d5fe16 Binary files /dev/null and b/lib/synthea_uscore_v4/hamcrest-core-1.3.jar differ diff --git a/lib/synthea_uscore_v4/hamcrest-json-0.2.jar b/lib/synthea_uscore_v4/hamcrest-json-0.2.jar new file mode 100644 index 0000000..406fa2f Binary files /dev/null and b/lib/synthea_uscore_v4/hamcrest-json-0.2.jar differ diff --git a/lib/synthea_uscore_v4/hapi-fhir-base-5.7.0.jar b/lib/synthea_uscore_v4/hapi-fhir-base-5.7.0.jar new file mode 100644 index 0000000..efea0ed Binary files /dev/null and b/lib/synthea_uscore_v4/hapi-fhir-base-5.7.0.jar differ diff --git a/lib/synthea_uscore_v4/hapi-fhir-client-5.7.0.jar b/lib/synthea_uscore_v4/hapi-fhir-client-5.7.0.jar new file mode 100644 index 0000000..67bab08 Binary files /dev/null and b/lib/synthea_uscore_v4/hapi-fhir-client-5.7.0.jar differ diff --git a/lib/synthea_uscore_v4/hapi-fhir-structures-dstu2-5.7.0.jar b/lib/synthea_uscore_v4/hapi-fhir-structures-dstu2-5.7.0.jar new file mode 100644 index 0000000..416a5b1 Binary files /dev/null and b/lib/synthea_uscore_v4/hapi-fhir-structures-dstu2-5.7.0.jar differ diff --git a/lib/synthea_uscore_v4/hapi-fhir-structures-dstu3-5.7.0.jar b/lib/synthea_uscore_v4/hapi-fhir-structures-dstu3-5.7.0.jar new file mode 100644 index 0000000..4abf7ba Binary files /dev/null and b/lib/synthea_uscore_v4/hapi-fhir-structures-dstu3-5.7.0.jar differ diff --git a/lib/synthea_uscore_v4/hapi-fhir-structures-r4-5.7.0.jar b/lib/synthea_uscore_v4/hapi-fhir-structures-r4-5.7.0.jar new file mode 100644 index 0000000..dca6e56 Binary files /dev/null and b/lib/synthea_uscore_v4/hapi-fhir-structures-r4-5.7.0.jar differ diff --git a/lib/synthea_uscore_v4/httpclient-4.5.13.jar b/lib/synthea_uscore_v4/httpclient-4.5.13.jar new file mode 100644 index 0000000..218ee25 Binary files /dev/null and b/lib/synthea_uscore_v4/httpclient-4.5.13.jar differ diff --git a/lib/synthea_uscore_v4/httpcore-4.4.13.jar b/lib/synthea_uscore_v4/httpcore-4.4.13.jar new file mode 100644 index 0000000..163dc43 Binary files /dev/null and b/lib/synthea_uscore_v4/httpcore-4.4.13.jar differ diff --git a/lib/synthea_uscore_v4/istack-commons-runtime-3.0.8.jar b/lib/synthea_uscore_v4/istack-commons-runtime-3.0.8.jar new file mode 100644 index 0000000..8f37e95 Binary files /dev/null and b/lib/synthea_uscore_v4/istack-commons-runtime-3.0.8.jar differ diff --git a/lib/synthea_uscore_v4/j2objc-annotations-1.3.jar b/lib/synthea_uscore_v4/j2objc-annotations-1.3.jar new file mode 100644 index 0000000..a429c72 Binary files /dev/null and b/lib/synthea_uscore_v4/j2objc-annotations-1.3.jar differ diff --git a/lib/synthea_uscore_v4/j2v8_linux_x86_64-4.6.0.jar b/lib/synthea_uscore_v4/j2v8_linux_x86_64-4.6.0.jar new file mode 100644 index 0000000..55ca7fa Binary files /dev/null and b/lib/synthea_uscore_v4/j2v8_linux_x86_64-4.6.0.jar differ diff --git a/lib/synthea_uscore_v4/j2v8_macosx_x86_64-4.6.0.jar b/lib/synthea_uscore_v4/j2v8_macosx_x86_64-4.6.0.jar new file mode 100644 index 0000000..bd8b222 Binary files /dev/null and b/lib/synthea_uscore_v4/j2v8_macosx_x86_64-4.6.0.jar differ diff --git a/lib/synthea_uscore_v4/j2v8_win32_x86-4.6.0.jar b/lib/synthea_uscore_v4/j2v8_win32_x86-4.6.0.jar new file mode 100644 index 0000000..88f1f75 Binary files /dev/null and b/lib/synthea_uscore_v4/j2v8_win32_x86-4.6.0.jar differ diff --git a/lib/synthea_uscore_v4/j2v8_win32_x86_64-4.6.0.jar b/lib/synthea_uscore_v4/j2v8_win32_x86_64-4.6.0.jar new file mode 100644 index 0000000..c9d344f Binary files /dev/null and b/lib/synthea_uscore_v4/j2v8_win32_x86_64-4.6.0.jar differ diff --git a/lib/synthea_uscore_v4/jackson-annotations-2.13.1.jar b/lib/synthea_uscore_v4/jackson-annotations-2.13.1.jar new file mode 100644 index 0000000..9bfca76 Binary files /dev/null and b/lib/synthea_uscore_v4/jackson-annotations-2.13.1.jar differ diff --git a/lib/synthea_uscore_v4/jackson-core-2.13.1.jar b/lib/synthea_uscore_v4/jackson-core-2.13.1.jar new file mode 100644 index 0000000..5955dd6 Binary files /dev/null and b/lib/synthea_uscore_v4/jackson-core-2.13.1.jar differ diff --git a/lib/synthea_uscore_v4/jackson-databind-2.13.1.jar b/lib/synthea_uscore_v4/jackson-databind-2.13.1.jar new file mode 100644 index 0000000..6a08d1f Binary files /dev/null and b/lib/synthea_uscore_v4/jackson-databind-2.13.1.jar differ diff --git a/lib/synthea_uscore_v4/jackson-dataformat-csv-2.13.1.jar b/lib/synthea_uscore_v4/jackson-dataformat-csv-2.13.1.jar new file mode 100644 index 0000000..3b7ef2d Binary files /dev/null and b/lib/synthea_uscore_v4/jackson-dataformat-csv-2.13.1.jar differ diff --git a/lib/synthea_uscore_v4/jackson-datatype-jsr310-2.13.1.jar b/lib/synthea_uscore_v4/jackson-datatype-jsr310-2.13.1.jar new file mode 100644 index 0000000..8011554 Binary files /dev/null and b/lib/synthea_uscore_v4/jackson-datatype-jsr310-2.13.1.jar differ diff --git a/lib/synthea_uscore_v4/jakarta.activation-api-1.2.1.jar b/lib/synthea_uscore_v4/jakarta.activation-api-1.2.1.jar new file mode 100644 index 0000000..bbfb52f Binary files /dev/null and b/lib/synthea_uscore_v4/jakarta.activation-api-1.2.1.jar differ diff --git a/lib/synthea_uscore_v4/jakarta.xml.bind-api-2.3.2.jar b/lib/synthea_uscore_v4/jakarta.xml.bind-api-2.3.2.jar new file mode 100644 index 0000000..b16236d Binary files /dev/null and b/lib/synthea_uscore_v4/jakarta.xml.bind-api-2.3.2.jar differ diff --git a/lib/synthea_uscore_v4/javaparser-1.0.11.jar b/lib/synthea_uscore_v4/javaparser-1.0.11.jar new file mode 100644 index 0000000..97f1937 Binary files /dev/null and b/lib/synthea_uscore_v4/javaparser-1.0.11.jar differ diff --git a/lib/synthea_uscore_v4/javax.activation-1.2.0.jar b/lib/synthea_uscore_v4/javax.activation-1.2.0.jar new file mode 100644 index 0000000..9637479 Binary files /dev/null and b/lib/synthea_uscore_v4/javax.activation-1.2.0.jar differ diff --git a/lib/synthea_uscore_v4/javax.activation-api-1.2.0.jar b/lib/synthea_uscore_v4/javax.activation-api-1.2.0.jar new file mode 100644 index 0000000..986c365 Binary files /dev/null and b/lib/synthea_uscore_v4/javax.activation-api-1.2.0.jar differ diff --git a/lib/synthea_uscore_v4/javax.json-1.0.4.jar b/lib/synthea_uscore_v4/javax.json-1.0.4.jar new file mode 100644 index 0000000..09967d8 Binary files /dev/null and b/lib/synthea_uscore_v4/javax.json-1.0.4.jar differ diff --git a/lib/synthea_uscore_v4/javax.persistence-2.1.0.jar b/lib/synthea_uscore_v4/javax.persistence-2.1.0.jar new file mode 100644 index 0000000..e48d2e9 Binary files /dev/null and b/lib/synthea_uscore_v4/javax.persistence-2.1.0.jar differ diff --git a/lib/synthea_uscore_v4/jaxb-api-2.4.0-b180830.0359.jar b/lib/synthea_uscore_v4/jaxb-api-2.4.0-b180830.0359.jar new file mode 100644 index 0000000..dcd345c Binary files /dev/null and b/lib/synthea_uscore_v4/jaxb-api-2.4.0-b180830.0359.jar differ diff --git a/lib/synthea_uscore_v4/jaxb-core-2.3.0.1.jar b/lib/synthea_uscore_v4/jaxb-core-2.3.0.1.jar new file mode 100644 index 0000000..431e143 Binary files /dev/null and b/lib/synthea_uscore_v4/jaxb-core-2.3.0.1.jar differ diff --git a/lib/synthea_uscore_v4/jaxb-impl-2.3.0.1.jar b/lib/synthea_uscore_v4/jaxb-impl-2.3.0.1.jar new file mode 100644 index 0000000..6ae03f6 Binary files /dev/null and b/lib/synthea_uscore_v4/jaxb-impl-2.3.0.1.jar differ diff --git a/lib/synthea_uscore_v4/jaxb-runtime-2.3.2.jar b/lib/synthea_uscore_v4/jaxb-runtime-2.3.2.jar new file mode 100644 index 0000000..62f8719 Binary files /dev/null and b/lib/synthea_uscore_v4/jaxb-runtime-2.3.2.jar differ diff --git a/lib/synthea_uscore_v4/jaxb2-basics-0.12.0.jar b/lib/synthea_uscore_v4/jaxb2-basics-0.12.0.jar new file mode 100644 index 0000000..e792e9a Binary files /dev/null and b/lib/synthea_uscore_v4/jaxb2-basics-0.12.0.jar differ diff --git a/lib/synthea_uscore_v4/jaxb2-basics-runtime-0.12.0.jar b/lib/synthea_uscore_v4/jaxb2-basics-runtime-0.12.0.jar new file mode 100644 index 0000000..de18fc3 Binary files /dev/null and b/lib/synthea_uscore_v4/jaxb2-basics-runtime-0.12.0.jar differ diff --git a/lib/synthea_uscore_v4/jaxb2-basics-tools-0.12.0.jar b/lib/synthea_uscore_v4/jaxb2-basics-tools-0.12.0.jar new file mode 100644 index 0000000..d2c3aad Binary files /dev/null and b/lib/synthea_uscore_v4/jaxb2-basics-tools-0.12.0.jar differ diff --git a/lib/synthea_uscore_v4/jcl-over-slf4j-1.7.33.jar b/lib/synthea_uscore_v4/jcl-over-slf4j-1.7.33.jar new file mode 100644 index 0000000..930ed49 Binary files /dev/null and b/lib/synthea_uscore_v4/jcl-over-slf4j-1.7.33.jar differ diff --git a/lib/synthea_uscore_v4/jfreechart-1.5.0.jar b/lib/synthea_uscore_v4/jfreechart-1.5.0.jar new file mode 100644 index 0000000..a6bebed Binary files /dev/null and b/lib/synthea_uscore_v4/jfreechart-1.5.0.jar differ diff --git a/lib/synthea_uscore_v4/jigsaw-2.2.6.jar b/lib/synthea_uscore_v4/jigsaw-2.2.6.jar new file mode 100644 index 0000000..1f7c9d6 Binary files /dev/null and b/lib/synthea_uscore_v4/jigsaw-2.2.6.jar differ diff --git a/lib/synthea_uscore_v4/jopt-simple-4.7.jar b/lib/synthea_uscore_v4/jopt-simple-4.7.jar new file mode 100644 index 0000000..f20b473 Binary files /dev/null and b/lib/synthea_uscore_v4/jopt-simple-4.7.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-1.5.jar b/lib/synthea_uscore_v4/jsbml-1.5.jar new file mode 100644 index 0000000..6456b18 Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-arrays-1.5.jar b/lib/synthea_uscore_v4/jsbml-arrays-1.5.jar new file mode 100644 index 0000000..5a115bd Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-arrays-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-comp-1.5.jar b/lib/synthea_uscore_v4/jsbml-comp-1.5.jar new file mode 100644 index 0000000..5ad6bc4 Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-comp-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-core-1.5.jar b/lib/synthea_uscore_v4/jsbml-core-1.5.jar new file mode 100644 index 0000000..6d84b49 Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-core-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-distrib-1.5.jar b/lib/synthea_uscore_v4/jsbml-distrib-1.5.jar new file mode 100644 index 0000000..76f6536 Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-distrib-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-dyn-1.5.jar b/lib/synthea_uscore_v4/jsbml-dyn-1.5.jar new file mode 100644 index 0000000..36b8e91 Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-dyn-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-fbc-1.5.jar b/lib/synthea_uscore_v4/jsbml-fbc-1.5.jar new file mode 100644 index 0000000..f89d687 Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-fbc-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-groups-1.5.jar b/lib/synthea_uscore_v4/jsbml-groups-1.5.jar new file mode 100644 index 0000000..ab94346 Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-groups-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-layout-1.5.jar b/lib/synthea_uscore_v4/jsbml-layout-1.5.jar new file mode 100644 index 0000000..a761497 Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-layout-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-multi-1.5.jar b/lib/synthea_uscore_v4/jsbml-multi-1.5.jar new file mode 100644 index 0000000..e456ec0 Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-multi-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-qual-1.5.jar b/lib/synthea_uscore_v4/jsbml-qual-1.5.jar new file mode 100644 index 0000000..379b1cb Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-qual-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-render-1.5.jar b/lib/synthea_uscore_v4/jsbml-render-1.5.jar new file mode 100644 index 0000000..8c50238 Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-render-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-req-1.5.jar b/lib/synthea_uscore_v4/jsbml-req-1.5.jar new file mode 100644 index 0000000..f429cdb Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-req-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-spatial-1.5.jar b/lib/synthea_uscore_v4/jsbml-spatial-1.5.jar new file mode 100644 index 0000000..34233c0 Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-spatial-1.5.jar differ diff --git a/lib/synthea_uscore_v4/jsbml-tidy-1.5.jar b/lib/synthea_uscore_v4/jsbml-tidy-1.5.jar new file mode 100644 index 0000000..425f65d Binary files /dev/null and b/lib/synthea_uscore_v4/jsbml-tidy-1.5.jar differ diff --git a/lib/synthea_uscore_v4/json-20090211.jar b/lib/synthea_uscore_v4/json-20090211.jar new file mode 100644 index 0000000..ef29094 Binary files /dev/null and b/lib/synthea_uscore_v4/json-20090211.jar differ diff --git a/lib/synthea_uscore_v4/json-path-2.4.0.jar b/lib/synthea_uscore_v4/json-path-2.4.0.jar new file mode 100644 index 0000000..6229306 Binary files /dev/null and b/lib/synthea_uscore_v4/json-path-2.4.0.jar differ diff --git a/lib/synthea_uscore_v4/json-simple-1.1.1.jar b/lib/synthea_uscore_v4/json-simple-1.1.1.jar new file mode 100644 index 0000000..dfd5856 Binary files /dev/null and b/lib/synthea_uscore_v4/json-simple-1.1.1.jar differ diff --git a/lib/synthea_uscore_v4/json-smart-2.3.jar b/lib/synthea_uscore_v4/json-smart-2.3.jar new file mode 100644 index 0000000..0cd52ea Binary files /dev/null and b/lib/synthea_uscore_v4/json-smart-2.3.jar differ diff --git a/lib/synthea_uscore_v4/jsonassert-1.1.1.jar b/lib/synthea_uscore_v4/jsonassert-1.1.1.jar new file mode 100644 index 0000000..7b69e08 Binary files /dev/null and b/lib/synthea_uscore_v4/jsonassert-1.1.1.jar differ diff --git a/lib/synthea_uscore_v4/jsr305-3.0.2.jar b/lib/synthea_uscore_v4/jsr305-3.0.2.jar new file mode 100644 index 0000000..59222d9 Binary files /dev/null and b/lib/synthea_uscore_v4/jsr305-3.0.2.jar differ diff --git a/lib/synthea_uscore_v4/jtidy-r938.jar b/lib/synthea_uscore_v4/jtidy-r938.jar new file mode 100644 index 0000000..a081c7c Binary files /dev/null and b/lib/synthea_uscore_v4/jtidy-r938.jar differ diff --git a/lib/synthea_uscore_v4/jul-to-slf4j-1.7.25.jar b/lib/synthea_uscore_v4/jul-to-slf4j-1.7.25.jar new file mode 100644 index 0000000..98d9668 Binary files /dev/null and b/lib/synthea_uscore_v4/jul-to-slf4j-1.7.25.jar differ diff --git a/lib/synthea_uscore_v4/junit-4.12.jar b/lib/synthea_uscore_v4/junit-4.12.jar new file mode 100644 index 0000000..3a7fc26 Binary files /dev/null and b/lib/synthea_uscore_v4/junit-4.12.jar differ diff --git a/lib/synthea_uscore_v4/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar b/lib/synthea_uscore_v4/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar new file mode 100644 index 0000000..45832c0 Binary files /dev/null and b/lib/synthea_uscore_v4/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar differ diff --git a/lib/synthea_uscore_v4/log4j-1.2-api-2.3.jar b/lib/synthea_uscore_v4/log4j-1.2-api-2.3.jar new file mode 100644 index 0000000..5712184 Binary files /dev/null and b/lib/synthea_uscore_v4/log4j-1.2-api-2.3.jar differ diff --git a/lib/synthea_uscore_v4/log4j-1.2.17.jar b/lib/synthea_uscore_v4/log4j-1.2.17.jar new file mode 100644 index 0000000..1d425cf Binary files /dev/null and b/lib/synthea_uscore_v4/log4j-1.2.17.jar differ diff --git a/lib/synthea_uscore_v4/log4j-api-2.17.0.jar b/lib/synthea_uscore_v4/log4j-api-2.17.0.jar new file mode 100644 index 0000000..e39dab0 Binary files /dev/null and b/lib/synthea_uscore_v4/log4j-api-2.17.0.jar differ diff --git a/lib/synthea_uscore_v4/log4j-core-2.17.0.jar b/lib/synthea_uscore_v4/log4j-core-2.17.0.jar new file mode 100644 index 0000000..b853057 Binary files /dev/null and b/lib/synthea_uscore_v4/log4j-core-2.17.0.jar differ diff --git a/lib/synthea_uscore_v4/model-1.3.17.jar b/lib/synthea_uscore_v4/model-1.3.17.jar new file mode 100644 index 0000000..3b18151 Binary files /dev/null and b/lib/synthea_uscore_v4/model-1.3.17.jar differ diff --git a/lib/synthea_uscore_v4/okhttp-3.8.1.jar b/lib/synthea_uscore_v4/okhttp-3.8.1.jar new file mode 100644 index 0000000..97b1fd4 Binary files /dev/null and b/lib/synthea_uscore_v4/okhttp-3.8.1.jar differ diff --git a/lib/synthea_uscore_v4/okio-1.13.0.jar b/lib/synthea_uscore_v4/okio-1.13.0.jar new file mode 100644 index 0000000..02c302f Binary files /dev/null and b/lib/synthea_uscore_v4/okio-1.13.0.jar differ diff --git a/lib/synthea_uscore_v4/org.abego.treelayout.core-1.0.1.jar b/lib/synthea_uscore_v4/org.abego.treelayout.core-1.0.1.jar new file mode 100644 index 0000000..2350adc Binary files /dev/null and b/lib/synthea_uscore_v4/org.abego.treelayout.core-1.0.1.jar differ diff --git a/lib/synthea_uscore_v4/org.hl7.fhir.dstu3-5.6.27.jar b/lib/synthea_uscore_v4/org.hl7.fhir.dstu3-5.6.27.jar new file mode 100644 index 0000000..1e3578f Binary files /dev/null and b/lib/synthea_uscore_v4/org.hl7.fhir.dstu3-5.6.27.jar differ diff --git a/lib/synthea_uscore_v4/org.hl7.fhir.r4-5.6.27.jar b/lib/synthea_uscore_v4/org.hl7.fhir.r4-5.6.27.jar new file mode 100644 index 0000000..db70a4f Binary files /dev/null and b/lib/synthea_uscore_v4/org.hl7.fhir.r4-5.6.27.jar differ diff --git a/lib/synthea_uscore_v4/org.hl7.fhir.utilities-5.6.27.jar b/lib/synthea_uscore_v4/org.hl7.fhir.utilities-5.6.27.jar new file mode 100644 index 0000000..29f80ba Binary files /dev/null and b/lib/synthea_uscore_v4/org.hl7.fhir.utilities-5.6.27.jar differ diff --git a/lib/synthea_uscore_v4/qdm-1.3.17.jar b/lib/synthea_uscore_v4/qdm-1.3.17.jar new file mode 100644 index 0000000..2375e12 Binary files /dev/null and b/lib/synthea_uscore_v4/qdm-1.3.17.jar differ diff --git a/lib/synthea_uscore_v4/quick-1.3.17.jar b/lib/synthea_uscore_v4/quick-1.3.17.jar new file mode 100644 index 0000000..85ca242 Binary files /dev/null and b/lib/synthea_uscore_v4/quick-1.3.17.jar differ diff --git a/lib/synthea_uscore_v4/serializer-2.7.2.jar b/lib/synthea_uscore_v4/serializer-2.7.2.jar new file mode 100644 index 0000000..10c881c Binary files /dev/null and b/lib/synthea_uscore_v4/serializer-2.7.2.jar differ diff --git a/lib/synthea_uscore_v4/slf4j-api-1.7.33.jar b/lib/synthea_uscore_v4/slf4j-api-1.7.33.jar new file mode 100644 index 0000000..db3730d Binary files /dev/null and b/lib/synthea_uscore_v4/slf4j-api-1.7.33.jar differ diff --git a/lib/synthea_uscore_v4/slf4j-log4j12-1.7.25.jar b/lib/synthea_uscore_v4/slf4j-log4j12-1.7.25.jar new file mode 100644 index 0000000..7d88a6e Binary files /dev/null and b/lib/synthea_uscore_v4/slf4j-log4j12-1.7.25.jar differ diff --git a/lib/synthea_uscore_v4/snakeyaml-1.27.jar b/lib/synthea_uscore_v4/snakeyaml-1.27.jar new file mode 100644 index 0000000..79bd430 Binary files /dev/null and b/lib/synthea_uscore_v4/snakeyaml-1.27.jar differ diff --git a/lib/synthea_uscore_v4/spring-beans-5.2.7.RELEASE.jar b/lib/synthea_uscore_v4/spring-beans-5.2.7.RELEASE.jar new file mode 100644 index 0000000..1760b95 Binary files /dev/null and b/lib/synthea_uscore_v4/spring-beans-5.2.7.RELEASE.jar differ diff --git a/lib/synthea_uscore_v4/spring-core-5.2.7.RELEASE.jar b/lib/synthea_uscore_v4/spring-core-5.2.7.RELEASE.jar new file mode 100644 index 0000000..80735c3 Binary files /dev/null and b/lib/synthea_uscore_v4/spring-core-5.2.7.RELEASE.jar differ diff --git a/lib/synthea_uscore_v4/spring-jcl-5.2.7.RELEASE.jar b/lib/synthea_uscore_v4/spring-jcl-5.2.7.RELEASE.jar new file mode 100644 index 0000000..c16a941 Binary files /dev/null and b/lib/synthea_uscore_v4/spring-jcl-5.2.7.RELEASE.jar differ diff --git a/lib/synthea_uscore_v4/spring-web-5.2.7.RELEASE.jar b/lib/synthea_uscore_v4/spring-web-5.2.7.RELEASE.jar new file mode 100644 index 0000000..fabb0a4 Binary files /dev/null and b/lib/synthea_uscore_v4/spring-web-5.2.7.RELEASE.jar differ diff --git a/lib/synthea_uscore_v4/stax-ex-1.8.1.jar b/lib/synthea_uscore_v4/stax-ex-1.8.1.jar new file mode 100644 index 0000000..a200db5 Binary files /dev/null and b/lib/synthea_uscore_v4/stax-ex-1.8.1.jar differ diff --git a/lib/synthea_uscore_v4/stax2-api-3.1.4.jar b/lib/synthea_uscore_v4/stax2-api-3.1.4.jar new file mode 100644 index 0000000..dded036 Binary files /dev/null and b/lib/synthea_uscore_v4/stax2-api-3.1.4.jar differ diff --git a/lib/synthea_uscore_v4/staxmate-2.3.0.jar b/lib/synthea_uscore_v4/staxmate-2.3.0.jar new file mode 100644 index 0000000..f9eea86 Binary files /dev/null and b/lib/synthea_uscore_v4/staxmate-2.3.0.jar differ diff --git a/lib/synthea_uscore_v4/synthea.jar b/lib/synthea_uscore_v4/synthea.jar new file mode 100644 index 0000000..c4b9c68 Binary files /dev/null and b/lib/synthea_uscore_v4/synthea.jar differ diff --git a/lib/synthea_uscore_v4/txw2-2.3.2.jar b/lib/synthea_uscore_v4/txw2-2.3.2.jar new file mode 100644 index 0000000..0d5ac01 Binary files /dev/null and b/lib/synthea_uscore_v4/txw2-2.3.2.jar differ diff --git a/lib/synthea_uscore_v4/ucum-1.0.2.jar b/lib/synthea_uscore_v4/ucum-1.0.2.jar new file mode 100644 index 0000000..744a98c Binary files /dev/null and b/lib/synthea_uscore_v4/ucum-1.0.2.jar differ diff --git a/lib/synthea_uscore_v4/validation-api-1.1.0.Final.jar b/lib/synthea_uscore_v4/validation-api-1.1.0.Final.jar new file mode 100644 index 0000000..de85403 Binary files /dev/null and b/lib/synthea_uscore_v4/validation-api-1.1.0.Final.jar differ diff --git a/lib/synthea_uscore_v4/woodstox-core-5.0.1.jar b/lib/synthea_uscore_v4/woodstox-core-5.0.1.jar new file mode 100644 index 0000000..f2c69bf Binary files /dev/null and b/lib/synthea_uscore_v4/woodstox-core-5.0.1.jar differ diff --git a/lib/synthea_uscore_v4/xalan-2.7.2.jar b/lib/synthea_uscore_v4/xalan-2.7.2.jar new file mode 100644 index 0000000..abdabe3 Binary files /dev/null and b/lib/synthea_uscore_v4/xalan-2.7.2.jar differ diff --git a/lib/synthea_uscore_v4/xml-apis-1.3.04.jar b/lib/synthea_uscore_v4/xml-apis-1.3.04.jar new file mode 100644 index 0000000..d42c0ea Binary files /dev/null and b/lib/synthea_uscore_v4/xml-apis-1.3.04.jar differ diff --git a/lib/synthea_uscore_v4/xml-apis-ext-1.3.04.jar b/lib/synthea_uscore_v4/xml-apis-ext-1.3.04.jar new file mode 100644 index 0000000..a7869d6 Binary files /dev/null and b/lib/synthea_uscore_v4/xml-apis-ext-1.3.04.jar differ diff --git a/lib/synthea_uscore_v4/xmlgraphics-commons-2.2.jar b/lib/synthea_uscore_v4/xmlgraphics-commons-2.2.jar new file mode 100644 index 0000000..d1813ff Binary files /dev/null and b/lib/synthea_uscore_v4/xmlgraphics-commons-2.2.jar differ diff --git a/lib/synthea_uscore_v4/xmlpull-1.1.3.1.jar b/lib/synthea_uscore_v4/xmlpull-1.1.3.1.jar new file mode 100644 index 0000000..cbc149d Binary files /dev/null and b/lib/synthea_uscore_v4/xmlpull-1.1.3.1.jar differ diff --git a/lib/synthea_uscore_v4/xpp3-1.1.4c.jar b/lib/synthea_uscore_v4/xpp3-1.1.4c.jar new file mode 100644 index 0000000..451ac82 Binary files /dev/null and b/lib/synthea_uscore_v4/xpp3-1.1.4c.jar differ diff --git a/lib/synthea_uscore_v4/xpp3_min-1.1.4c.jar b/lib/synthea_uscore_v4/xpp3_min-1.1.4c.jar new file mode 100644 index 0000000..813a9a8 Binary files /dev/null and b/lib/synthea_uscore_v4/xpp3_min-1.1.4c.jar differ diff --git a/lib/synthea_uscore_v4/xpp3_xpath-1.1.4c.jar b/lib/synthea_uscore_v4/xpp3_xpath-1.1.4c.jar new file mode 100644 index 0000000..64a7b48 Binary files /dev/null and b/lib/synthea_uscore_v4/xpp3_xpath-1.1.4c.jar differ diff --git a/lib/synthea_uscore_v4/xstream-1.4.9.jar b/lib/synthea_uscore_v4/xstream-1.4.9.jar new file mode 100644 index 0000000..c754e0a Binary files /dev/null and b/lib/synthea_uscore_v4/xstream-1.4.9.jar differ diff --git a/lib/v3/choice_types.rb b/lib/v3/choice_types.rb new file mode 100644 index 0000000..69e6d9e --- /dev/null +++ b/lib/v3/choice_types.rb @@ -0,0 +1,51 @@ +module DataScript + # Version specific constants go here, other methods go in ../choice_type_creator.rb + class ChoiceTypeCreator + MUST_SUPPORT_CHOICE_TYPES = { + FHIR::DiagnosticReport => + { + prefix: 'effective', + suffixes: %w[DateTime Period], + profiles: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-note', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab' + ] + }, + FHIR::Immunization => + { + prefix: 'occurrence', + suffixes: %w[DateTime String], + profiles: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-immunization' + ] + }, + FHIR::Observation => + { + prefix: 'effective', + suffixes: %w[DateTime Period], + profiles: [ + 'http://hl7.org/fhir/StructureDefinition/resprate', + 'http://hl7.org/fhir/StructureDefinition/heartrate', + 'http://hl7.org/fhir/StructureDefinition/bodyweight', + 'http://hl7.org/fhir/StructureDefinition/bp', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-weight-for-height', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-bmi-for-age', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry', + 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile', + 'http://hl7.org/fhir/StructureDefinition/bodyheight', + 'http://hl7.org/fhir/StructureDefinition/bodytemp' + ] + }, + FHIR::Procedure => + { + prefix: 'performed', + suffixes: %w[DateTime Period], + profiles: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure' + ] + } + }.freeze + end +end \ No newline at end of file diff --git a/lib/constraints.rb b/lib/v3/constraints.rb similarity index 95% rename from lib/constraints.rb rename to lib/v3/constraints.rb index 78a57d4..7e1c42a 100644 --- a/lib/constraints.rb +++ b/lib/v3/constraints.rb @@ -132,8 +132,10 @@ def self.ethnicity(bundle) def self.smoker(bundle) entries = bundle.entry.select {|entry| entry.resource.is_a?(FHIR::Observation)} observations = entries.map {|entry| entry.resource} - smoking_statuses = observations.select {|observation| observation.code.text == 'Tobacco smoking status NHIS'} - smoking_statuses.map {|status| status.value.text}.include? 'Current every day smoker' + smoking_statuses = observations.select {|observation| observation.code.text&.start_with?('Tobacco smoking status')} + altA = smoking_statuses.map {|status| status.value.text}.include? 'Current every day smoker' + altB = smoking_statuses.map {|status| status.value.text}.include? 'Smokes tobacco daily (finding)' + (altA || altB) end def self.has(bundle, fhir_class) diff --git a/lib/modifications.rb b/lib/v3/modifications.rb similarity index 95% rename from lib/modifications.rb rename to lib/v3/modifications.rb index 305dc86..3fd8bc4 100644 --- a/lib/modifications.rb +++ b/lib/v3/modifications.rb @@ -3,7 +3,7 @@ require 'base64' require 'securerandom' require_relative 'constraints' -require_relative 'choice_type_creator' +require_relative '../choice_type_creator' require 'fhir_models' require 'time' @@ -63,7 +63,7 @@ def self.modify!(results, random_seed = 3) # Only delete it if it's not somehow important if !references.include?(e.resource.id) && - !(e.resource.is_a?(FHIR::Observation) && e.resource&.code&.text == 'Tobacco smoking status NHIS') && + !(e.resource.is_a?(FHIR::Observation) && e.resource&.code&.text.start_with?('Tobacco smoking status')) && (missing_profiles & profiles).empty? deleted_ids << e.resource.id elsif !(missing_profiles & profiles).empty? @@ -150,15 +150,15 @@ def self.modify!(results, random_seed = 3) end # sort the results by number of Conditions - results.sort do |a,b| + results.sort! do |a,b| count_a = a.entry.count {|e| e.resource.resourceType == 'Condition'} count_b = b.entry.count {|e| e.resource.resourceType == 'Condition'} count_a <=> count_b end # select the person with the most Conditions selection_conditions = results.last - alter_condition(selection_conditions, rng) - puts " - Altered Condition: #{selection_conditions.entry.first.resource.id}" + altered = alter_condition(selection_conditions, rng) + puts " - Altered Condition: #{altered.id}" # select someone with the most numerous gender # from the people remaining, and remove their name @@ -288,7 +288,7 @@ def self.modify!(results, random_seed = 3) dev = FHIR::Device.new(device.resource.to_hash) dev.id = SecureRandom.uuid dev.udiCarrier.first.carrierHRF = nil - barcode_file_path = File.join(File.dirname(__FILE__), './barcode.png') + barcode_file_path = File.join(File.dirname(__FILE__), '../barcode.png') barcode_file = File.open(barcode_file_path, 'rb') barcode_data = barcode_file.read barcode_file.close @@ -316,7 +316,7 @@ def self.modify!(results, random_seed = 3) # select Bundle with Pulse Oximetry selection_pulse_ox = results.find {|b| DataScript::Constraints.has_pulse_ox(b)} if selection_pulse_ox - provenance = selection_device.entry.find { |e| e.resource.resourceType == 'Provenance' } + provenance = selection_pulse_ox.entry.find { |e| e.resource.resourceType == 'Provenance' } pulse_ox_entry = selection_pulse_ox.entry.find {|e| e.resource&.meta&.profile&.include? 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry' } pulse_ox_clone = nil 2.times do @@ -460,6 +460,8 @@ def self.modify!(results, random_seed = 3) end else instance.valueQuantity = nil + instance.valueCodeableConcept = nil + instance.valueString = nil end new_entry = create_bundle_entry(instance) provenance.target << FHIR::Reference.new @@ -521,9 +523,6 @@ def self.modify!(results, random_seed = 3) dr.performer << { reference: "urn:uuid:#{dr_organization}" } end - # Add Group - results << create_group(results) - # The JSON from this exported patient will need to be manually altered to # create primitive extensions, so we specifically return just this patient bundle. selection_name @@ -563,6 +562,7 @@ def self.alter_condition(bundle, rng) unknown = FHIR::CodeableConcept.new unknown.extension = [ data_absent_reason ] random_condition.category = [ unknown ] + random_condition end def self.data_absent_reason @@ -584,27 +584,6 @@ def self.alter_smoking_status(bundle) last_smoking_observation.valueCodeableConcept = smoker end - def self.create_group(results) - group = FHIR::Group.new - group.id = SecureRandom.uuid - group.identifier = [ FHIR::Identifier.new ] - group.identifier.first.system = 'urn:ietf:rfc:3986' - group.identifier.first.value = "urn:uuid:#{group.id}" - group.active = true - group.type = 'person' - group.actual = true - group.name = 'Synthea US Core Patients' - group.quantity = results.length - group.member = [] - results.each do |bundle| - group_member = FHIR::Group::Member.new - group_member.entity = FHIR::Reference.new - group_member.entity.reference = bundle.entry.first.fullUrl - group.member << group_member - end - group - end - def self.create_codeable_concept(system, code, display) coding = FHIR::Coding.new coding.system = system @@ -626,15 +605,6 @@ def self.create_bundle_entry(resource) entry end - def self.get_reference_type(bundle, reference_string) - # reference_string will be in a format like: - # urn:uuid:1234-abcd-1234-abcd - # So splitting on `:` and taking last gets us just the UUID - # which is also the ID of the referenced resource - id = reference_string.split(':').last - bundle.entry.find { |e| e.resource.id == id }&.resource&.class - end - def self.get_resource_counts(bundle) resource_counts = bundle.entry.each_with_object({}) do |entry, rc| resource_type = entry.resource.resourceType diff --git a/lib/v4/choice_types.rb b/lib/v4/choice_types.rb new file mode 100644 index 0000000..14ee3ec --- /dev/null +++ b/lib/v4/choice_types.rb @@ -0,0 +1,54 @@ +module DataScript + # Version specific constants go here, other methods go in ../choice_type_creator.rb + class ChoiceTypeCreator + MUST_SUPPORT_CHOICE_TYPES = { + FHIR::DiagnosticReport => + { + prefix: 'effective', + suffixes: %w[DateTime Period], + profiles: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-note', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab' + ] + }, + FHIR::Immunization => + { + prefix: 'occurrence', + suffixes: %w[DateTime String], + profiles: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-immunization' + ] + }, + FHIR::Observation => + { + prefix: 'effective', + suffixes: %w[DateTime], + profiles: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-vital-signs', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-blood-pressure', #'http://hl7.org/fhir/StructureDefinition/bp' + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-bmi', #'http://hl7.org/fhir/StructureDefinition/bmi', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-head-circumference', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-height', #'http://hl7.org/fhir/StructureDefinition/bodyheight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-weight', #'http://hl7.org/fhir/StructureDefinition/bodyweight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-temperature', #'http://hl7.org/fhir/StructureDefinition/bodytemp', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-heart-rate', #'http://hl7.org/fhir/StructureDefinition/heartrate', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-bmi-for-age', + 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-weight-for-height', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-respiratory-rate' #'http://hl7.org/fhir/StructureDefinition/resprate', + # 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus' + ] + }, + FHIR::Procedure => + { + prefix: 'performed', + suffixes: %w[DateTime], + profiles: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure' + ] + } + }.freeze + end +end \ No newline at end of file diff --git a/lib/v4/constraints.rb b/lib/v4/constraints.rb new file mode 100644 index 0000000..abcd24b --- /dev/null +++ b/lib/v4/constraints.rb @@ -0,0 +1,166 @@ +require 'date' + +module DataScript + class Constraints + + CONSTRAINTS = { + 'one_male' => lambda {|results| results.any? {|bundle| patient(bundle) && gender(bundle) == 'male'}}, + 'one_female' => lambda {|results| results.any? {|bundle| gender(bundle) == 'female'}}, + 'one_child' => lambda {|results| results.any? {|bundle| age = age(bundle); age >= 0 && age < 18 }}, + 'child_has_immunizations' => lambda {|results| results.any? {|bundle| age = age(bundle); (age >= 0 && age < 18) && has(bundle, FHIR::Immunization) }}, + 'child_does_not_smoke' => lambda {|results| results.any? {|bundle| age = age(bundle); (age >= 0 && age < 18) && !smoker(bundle) }}, + 'one_adult' => lambda {|results| results.any? {|bundle| age = age(bundle); age >= 18 && age <= 65 }}, + 'one_elder' => lambda {|results| results.any? {|bundle| age = age(bundle); age > 65 }}, + 'elder_has_device' => lambda {|results| results.any? {|bundle| age = age(bundle); (age > 65) && has(bundle, FHIR::Device) }}, + 'elder_is_alive' => lambda {|results| results.any? {|bundle| age = age(bundle); (age > 65) && alive(bundle) }}, + 'one_white' => lambda {|results| results.any? {|bundle| race(bundle) == 'White'}}, + 'one_black' => lambda {|results| results.any? {|bundle| race(bundle) == 'Black or African American'}}, + 'one_hispanic' => lambda {|results| results.any? {|bundle| ethnicity(bundle) == 'Hispanic or Latino'}}, + 'one_smoker' => lambda {|results| results.any? {|bundle| smoker(bundle) }}, + 'one_organization' => lambda {|results| results.any? {|bundle| has(bundle, FHIR::Organization) }}, + 'one_practitioner' => lambda {|results| results.any? {|bundle| has(bundle, FHIR::Practitioner) }}, + 'observation_code' => lambda {|results| results.any? {|bundle| bundle.entry.any? {|entry| entry.resource.resourceType == 'Observation' && !entry.resource.valueCodeableConcept.nil?} }}, + 'observation_quantity' => lambda {|results| results.any? {|bundle| bundle.entry.any? {|entry| entry.resource.resourceType == 'Observation' && !entry.resource.valueQuantity.nil?} }}, + 'observation_string' => lambda {|results| results.any? {|bundle| bundle.entry.any? {|entry| entry.resource.resourceType == 'Observation' && !entry.resource.valueString.nil?} }} + } + + CONSTRAINTS_MRBURNS = { + 'has_allergy' => lambda {|results| results.any? {|bundle| has(bundle, FHIR::AllergyIntolerance) }}, + 'has_pulse_ox' => lambda {|results| results.any? {|bundle| has_pulse_ox(bundle) }}, + } + + CONSTRAINTS_MRBURNS_DOES_NOT_NEED = [ + 'one_female', + 'one_child', + 'child_has_immunizations', + 'child_does_not_smoke', + 'one_adult', + 'elder_is_alive', + 'one_white', + 'one_black', + 'one_hispanic', + 'one_smoker', + ] + + REQUIRED_PROFILES = [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-allergyintolerance', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-careplan', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-careteam', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-note', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-documentreference', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-encounter', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-goal', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-immunization', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-implantable-device', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-location', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-medication', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-organization', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitionerrole', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-provenance', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-bmi-for-age', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-weight-for-height', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-head-circumference', + 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-respiratory-rate', #'http://hl7.org/fhir/StructureDefinition/resprate', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-heart-rate', #'http://hl7.org/fhir/StructureDefinition/heartrate', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-temperature', #'http://hl7.org/fhir/StructureDefinition/bodytemp', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-height', #'http://hl7.org/fhir/StructureDefinition/bodyheight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-weight', #'http://hl7.org/fhir/StructureDefinition/bodyweight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-bmi', #'http://hl7.org/fhir/StructureDefinition/bmi', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-blood-pressure' #'http://hl7.org/fhir/StructureDefinition/bp' + ] + + attr_accessor :violations + + def initialize + @violations = [] + end + + def satisfied?(results, keys = CONSTRAINTS.keys) + @violations.clear + constraints = CONSTRAINTS.keep_if {|key, test| keys.include?(key)} + constraints.each do |key, test| + test_result = test.call(results) + unless test_result + @violations << key + end + end + @violations.empty? + end + + def profiles_present(results) + present = results.map {|b| b.entry.map {|e| e.resource&.meta&.profile }}.flatten.uniq + present.delete(nil) + present + end + + def self.patient(bundle) + bundle.entry.each do |entry| + return entry.resource if entry.resource.is_a?(FHIR::Patient) + end + nil + end + + def self.gender(bundle) + self.patient(bundle)&.gender + end + + def self.age(bundle) + birth_date = self.patient(bundle)&.birthDate + return -Float::INFINITY if birth_date.nil? + date = Date.parse(birth_date) + age = Date.today.year - date.year + age -= 1 if Date.today < date.next_year(age) + age + end + + def self.alive(bundle) + p = self.patient(bundle) + ((p&.deceasedBoolean.nil? || p&.deceasedBoolean == false) && p&.deceasedDateTime.nil?) + end + + def self.race(bundle) + self.patient(bundle)&.us_core_race&.ombCategory&.display rescue nil + end + + def self.ethnicity(bundle) + self.patient(bundle)&.us_core_ethnicity&.ombCategory&.display rescue nil + end + + def self.smoker(bundle) + entries = bundle.entry.select {|entry| entry.resource.is_a?(FHIR::Observation)} + observations = entries.map {|entry| entry.resource} + smoking_statuses = observations.select {|observation| observation.code.text&.start_with?('Tobacco smoking status')} + altA = smoking_statuses.map {|status| status.value.text}.include? 'Current every day smoker' + altB = smoking_statuses.map {|status| status.value.text}.include? 'Smokes tobacco daily (finding)' + (altA || altB) + end + + def self.has(bundle, fhir_class) + bundle.entry.any? {|entry| entry.resource.is_a?(fhir_class)} + end + + def self.has_pulse_ox(bundle) + bundle.entry.any? do |entry| + entry.resource&.meta&.profile&.include? 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry' + end + end + + def self.has_headcircum(results) + results.any? do |bundle| + bundle.entry.any? do |entry| + entry.resource&.meta&.profile&.include? 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile' + end + end + end + end +end diff --git a/lib/v4/modifications.rb b/lib/v4/modifications.rb new file mode 100644 index 0000000..d009e82 --- /dev/null +++ b/lib/v4/modifications.rb @@ -0,0 +1,722 @@ +# frozen_string_literal: true + +require 'base64' +require 'securerandom' +require_relative 'constraints' +require_relative '../choice_type_creator' +require 'fhir_models' +require 'time' + +module DataScript + class Modifications + DESIRED_MAX = 20 + + def self.modify!(results, random_seed = 3) + FHIR.logger.level = :info + + # Create a random number generator, to pass to things that need randomness + rng = Random.new(random_seed) + # results is an Array of FHIR::Bundle objects, + # where the first resource is a Patient. + + # Remove unwanted patient extensions and identifers + results.each do |bundle| + if bundle.entry.first.resource.resourceType == 'Patient' + # first, remove unwanted patient extensions + bundle.entry.first.resource.extension.delete_if do |extension| + extension.url.start_with? 'http://synthetichealth.github.io' + end + + # next, remove unwanted patient identifier + bundle.entry.first.resource.identifier.delete_if do |identifier| + identifier.system.start_with? 'http://standardhealthrecord.org' + end + + # make sure every patient has a postalCode + # some towns do not have postalCodes + bundle.entry.first.resource.address.first.postalCode = '01999' unless bundle.entry.first.resource.address.first.postalCode + + # finally, make sure every patient has an address period + bundle.entry.first.resource.address.first.period = FHIR::Period.new + bundle.entry.first.resource.address.first.period.start = bundle.entry.first.resource.birthDate + else + # Not a patient bundle, probably contains Organizations, Locations, Practitioners, and/or PractitionerRoles + # Remove unwanted extensions + bundle.entry.each do |entry| + entry.resource.extension.delete_if { |extension| extension.url.start_with? 'http://synthetichealth.github.io' } + end + end + end + + # Make sure there aren't stupid numbers of every resource type + missing_profiles = DataScript::Constraints::REQUIRED_PROFILES.dup + results.each do |bundle| + resource_counts = get_resource_counts(bundle) + provenance = bundle.entry.find { |e| e.resource.is_a? FHIR::Provenance }&.resource + next unless provenance + resource_counts.each do |type, count| + next unless count >= DESIRED_MAX + + deleted_ids = [] + dr_observations = get_diagreport_referenced_observations(bundle) + dr_notes = get_docref_referenced_attachments(bundle) + encounter_refs = get_referenced_encounters(bundle) + reason_refs = get_referenced_reasons(bundle) + addresses_refs = get_addresses(bundle) + medication_refs = get_medreqs_with_med_references(bundle) + references = (dr_observations + dr_notes + encounter_refs + reason_refs + addresses_refs + medication_refs).compact.uniq + bundle.entry.find_all { |e| e.resource.resourceType == type }.shuffle(random: rng).each do |e| + break if deleted_ids.count >= (count - DESIRED_MAX) + + profiles = e.resource&.meta&.profile || [] + + # Only delete it if it's not somehow important + if !references.include?(e.resource.id) && + !(e.resource.is_a?(FHIR::Observation) && e.resource&.code&.text.start_with?('Tobacco smoking status')) && + (missing_profiles & profiles).empty? + deleted_ids << e.resource.id + elsif !(missing_profiles & profiles).empty? + missing_profiles -= profiles + end + end + bundle.entry.delete_if { |e| deleted_ids.include? e.resource.id } + remove_provenance_targets(deleted_ids, provenance) + end + end + + # add reaction.manifestation to allergy intolerance result because of must support changes in 3.1.1 + already_contains_reaction_manifestation = + results.any? do |bundle| + bundle + .entry + .select { |e| e.resource.is_a? FHIR::AllergyIntolerance } + .map(&:resource) + .any? do |resource| + resource.reaction.any? { |reaction| reaction.manifestation.any? } + end + end + unless already_contains_reaction_manifestation + results.each do |bundle| + allergy_intoleranace_resource = bundle.entry.find { |e| e.resource.is_a? FHIR::AllergyIntolerance }&.resource + next if allergy_intoleranace_resource.nil? + + reaction = FHIR::AllergyIntolerance::Reaction.new + manifestation = create_codeable_concept('http://snomed.info/sct', '271807003', 'skin rash') + reaction.manifestation << manifestation + allergy_intoleranace_resource.reaction << reaction + puts " - Altered AllergyIntolerance: #{allergy_intoleranace_resource.id}" + break + end + end + + # Add discharge disposition to every encounter referenced by a medicationRequest of each record + # This is necessary (rather than just one) because of how Inferno Program has to get Encounters + results.each do |bundle| + encounter_urls = bundle.entry.find_all { |e| e.resource.resourceType == 'MedicationRequest' }.map { |e| e.resource&.encounter&.reference }.compact.uniq + encounter_urls.each do |encounter_url| + encounter_entry = bundle.entry.find { |e| e.fullUrl == encounter_url } + encounter = encounter_entry.resource + encounter.hospitalization = FHIR::Encounter::Hospitalization.new + encounter.hospitalization.dischargeDisposition = create_codeable_concept('http://www.nubc.org/patient-discharge','01','Discharged to home care or self care (routine discharge)') + end + end + + # Make sure at least one organization has an NPI + result = results.find {|b| DataScript::Constraints.has(b, FHIR::Organization)} + organization_entry = result.entry.find { |e| e.resource.resourceType == 'Organization' } + organization = organization_entry.resource + # Add a 10 digit NPI + organization.identifier << FHIR::Identifier.new + organization.identifier.last.system = 'http://hl7.org/fhir/sid/us-npi' + organization.identifier.last.value = '9999999999' + # Add a CLIA + organization.identifier << FHIR::Identifier.new + organization.identifier.last.system = 'urn:oid:2.16.840.1.113883.4.7' + organization.identifier.last.value = '9999999999' + + # Add a PractitionerRole.endpoint + result = results.find {|b| DataScript::Constraints.has(b, FHIR::PractitionerRole)} + pr_entry = result.entry.find { |e| e.resource.resourceType == 'PractitionerRole' } + pr = pr_entry.resource + pr.endpoint = [ FHIR::Reference.new ] + pr.endpoint.first.reference = '#endpoint' + pr.endpoint.first.type = 'Endpoint' + endpoint = FHIR::Endpoint.new + endpoint.id = 'endpoint' + endpoint.status = 'active' + endpoint.connectionType = create_codeable_concept('http://terminology.hl7.org/CodeSystem/endpoint-connection-type', 'direct-project', 'Direct Project').coding.first + endpoint.payloadType = [ create_codeable_concept('http://terminology.hl7.org/CodeSystem/endpoint-payload-type', 'any', 'Any') ] + endpoint.address = "mailto:#{pr.telecom.last.value}" + pr.contained = [endpoint] + + # select by smoking status + selection_smoker = results.find {|b| DataScript::Constraints.smoker(b)} + unless selection_smoker + # if there is no smoker, choose the oldest patient + # and make them start smoking at the end of their life... + # because why not? + patient_results = results.find_all {|b| b.entry.first.resource.resourceType == 'Patient'} + oldest = patient_results.sort {|a,b| a.entry.first.resource.birthDate <=> b.entry.first.resource.birthDate }.first + alter_smoking_status(oldest) + puts " - Altered Smoker: #{oldest.entry.first.resource.id}" + end + + # sort the results by number of Conditions + results.sort! do |a,b| + count_a = a.entry.count {|e| e.resource.resourceType == 'Condition'} + count_b = b.entry.count {|e| e.resource.resourceType == 'Condition'} + count_a <=> count_b + end + # select the person with the most Conditions + selection_conditions = results.last + altered = alter_condition(selection_conditions, rng) + puts " - Altered Condition: #{altered.id}" + + # select someone with the most numerous gender + # from the people remaining, and remove their name + unless MRBURNS + selection_name = pick_by_gender(results) + remove_name(selection_name) + puts " - Altered Name: #{selection_name.entry.first.resource.id}" + end + + # select by clinical note + selection_note = results.find {|b| DataScript::Constraints.has(b, FHIR::DocumentReference)} + if selection_note + # modify it to have a URL rather than base64 encoded data + docref = selection_note.entry.reverse.find { |e| e.resource.resourceType == 'DocumentReference' }.resource + report = selection_note.entry.reverse.find { |e| + e&.resource&.resourceType == 'DiagnosticReport' && + e&.resource&.presentedForm&.first&.data == docref&.content&.first&.attachment&.data }&.resource + url = 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf' + docref.content.first.attachment.contentType = 'application/pdf' + docref.content.first.attachment.data = nil + docref.content.first.attachment.url = url + report.presentedForm.first.contentType = 'application/pdf' + report.presentedForm.first.data = nil + report.presentedForm.first.url = url + puts " - Altered DocumentReference URL: #{docref.id}" + puts " - Altered DiagnosticReport URL: #{report.id}" + else + puts " * FAILED to find DocumentReference!" + end + + # collect all the clinical notes and modify codes so we have at least one of each type + category_types = [ + [ 'Cardiology', 'LP29708-2' ], + [ 'Pathology', 'LP7839-6' ], + [ 'Radiology', 'LP29684-5' ] + ] + note_types = [ + [ 'Consult note', '11488-4' ], + [ 'Discharge summary', '18842-5' ], + [ 'History and physical note', '34117-2' ], + [ 'Procedure note', '28570-0' ], + [ 'Progress note', '11506-3' ], + [ 'Diagnostic imaging study', '18748-4' ], + [ 'Laboratory report', '11502-2' ], + [ 'Pathology study', '11526-1' ], + [ 'Referral note', '57133-1' ], + [ 'Surgical operation note', '11504-8' ], + [ 'Nurse Note', '34746-8' ] + ] + category_types.map! {|type| create_codeable_concept('http://loinc.org',type.last,type.first) } + note_types.map! {|type| create_codeable_concept('http://loinc.org',type.last,type.first) } + # grab all the clinical notes + all_docref = results.map {|b| b.entry.select {|e| e.resource.resourceType == 'DocumentReference'}.map {|e| e.resource} }.flatten + all_report = results.map {|b| b.entry.select {|e| e.resource.resourceType == 'DiagnosticReport'}.map {|e| e.resource} }.flatten + # there are more DiagnosticReports than DocumentReferences, + # so we need to filter them... + docref_data = all_docref.map {|r| r.content.first.attachment.data} + matching_report = all_report.select { |r| r.presentedForm.length >= 1 && docref_data.include?(r.presentedForm.first.data) } + + # need to replace the codes... + # we will use a uniform distribution of note_types + note_types_index = 0 + category_types_index = 0 + all_docref.zip(matching_report).each do |docref, report| + break if report.nil? + docref.type = note_types[note_types_index] + report.category = [ category_types[category_types_index] ] + report.code = note_types[note_types_index] + note_types_index += 1 + category_types_index += 1 + note_types_index = 0 if note_types_index >= note_types.length + category_types_index = 0 if category_types_index >= category_types.length + end + puts " - Altered codes for #{all_docref.length} clinical notes." + + all_docref.each do |docref| + docref&.identifier&.each {|id| id.value = "urn:uuid:#{id.value}" if (id.system == 'urn:ietf:rfc:3986' && !id.value&.start_with?('urn'))} + end + puts " - Altered identifiers for #{all_docref.length} clinical notes." + + # select by medication + selection_medication = results.find {|b| DataScript::Constraints.has(b, FHIR::Medication)} + unless selection_medication + # if there is no free-standing Medication resource, we need to make one. + med_bundle = results.find { |b| DataScript::Constraints.has(b, FHIR::MedicationRequest) } + medreq = med_bundle.entry.find { |e| e.resource.resourceType == 'MedicationRequest' } + provenance = med_bundle.entry.find { |e| e.resource.resourceType == 'Provenance' } + # create the medication + med = FHIR::Medication.new + med.id = SecureRandom.uuid + med.status = 'active' + med.meta = FHIR::Meta.new + med.meta.profile = [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-medication' ] + med.code = medreq.resource.medicationCodeableConcept + # alter the MedicationRequest to refer to the Medication resource and not a code + medreq.resource.reportedBoolean = true + medreq.resource.medicationCodeableConcept = nil + medreq.resource.medicationReference = FHIR::Reference.new + medreq.resource.medicationReference.reference = "urn:uuid:#{med.id}" + medreq.resource.status = 'active' + # add the Medication as a new Bundle entry + med_bundle.entry << create_bundle_entry(med) + # add the Medication into the provenance + provenance.resource.target << FHIR::Reference.new + provenance.resource.target.last.reference = "urn:uuid:#{med.id}" + puts " - Altered Medication: #{med_bundle.entry.first.resource.id}" + end + + # change one medication request from an order to a self prescription, if needed + # we need at least two intents in order to demonstrate the multi-or search requirement for intent + any_non_order = results.any? {|b| b.entry.any?{|e| e.resource.resourceType == 'MedicationRequest' && e.resource.intent != 'order' }} + selection_medication_request = results.find {|b| DataScript::Constraints.has(b, FHIR::MedicationRequest)} + if !any_non_order && selection_medication_request + changed_medication = selection_medication_request.entry.select { |e| e.resource.resourceType == 'MedicationRequest'}.last.resource + changed_medication.intent = 'plan' + changed_medication.reportedBoolean = true + changed_medication.reportedReference = nil + changed_medication.requester = changed_medication.subject.clone + changed_medication.encounter = nil + puts " - Altered Medication Request to have 'plan' intent: #{changed_medication.id}" + end + + # select by device + selection_device = results.find {|b| DataScript::Constraints.has(b, FHIR::Device)} + if selection_device + # if there is a Device resource, we need to clone it and use carrierAIDC. + device = selection_device.entry.find { |e| e.resource.resourceType == 'Device' } + provenance = selection_device.entry.find { |e| e.resource.resourceType == 'Provenance' } + # create the new Device + dev = FHIR::Device.new(device.resource.to_hash) + dev.id = SecureRandom.uuid + dev.udiCarrier.first.carrierHRF = nil + barcode_file_path = File.join(File.dirname(__FILE__), '../barcode.png') + barcode_file = File.open(barcode_file_path, 'rb') + barcode_data = barcode_file.read + barcode_file.close + dev.udiCarrier.first.carrierAIDC = Base64.encode64(barcode_data) + # add the Device as a new Bundle entry + selection_device.entry << create_bundle_entry(dev) + # add the Device into the provenance + provenance.resource.target << FHIR::Reference.new + provenance.resource.target.last.reference = "urn:uuid:#{dev.id}" + puts " - Cloned Device: #{selection_device.entry.first.resource.id}" + end + + # select an Immunization + selection_immunization = results.find {|b| DataScript::Constraints.has(b, FHIR::Immunization)} + if selection_immunization + # if there is an Immunization resource, we need to clone it and use carrierAIDC. + immunization_entry = selection_immunization.entry.find { |e| e.resource.resourceType == 'Immunization' } + immunization = immunization_entry.resource + immunization.vaccineCode = create_codeable_concept('http://terminology.hl7.org/CodeSystem/data-absent-reason', 'unknown', 'Unknown') + immunization.statusReason = create_codeable_concept('http://terminology.hl7.org/CodeSystem/v3-ActReason', 'OSTOCK', 'product out of stock') + immunization.status = 'not-done' + puts " - Altered Immunization: #{selection_immunization.entry.first.resource.id}" + end + + # select Bundle with Pulse Oximetry + selection_pulse_ox = results.find {|b| DataScript::Constraints.has_pulse_ox(b)} + if selection_pulse_ox + provenance = selection_pulse_ox.entry.find { |e| e.resource.resourceType == 'Provenance' } + pulse_ox_entry = selection_pulse_ox.entry.find {|e| e.resource&.meta&.profile&.include? 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry' } + pulse_ox_clone = nil + 2.times do + pulse_ox_clone = FHIR.from_contents(pulse_ox_entry.resource.to_json) + pulse_ox_clone.id = SecureRandom.uuid + # Add the must support components + pulse_ox_clone.component = [] + # First component is flow rate + pulse_ox_clone.component << FHIR::Observation::Component.new + pulse_ox_clone.component.last.code = create_codeable_concept('http://loinc.org','3151-8', 'Inhaled oxygen flow rate') + pulse_ox_clone.component.last.valueQuantity = FHIR::Quantity.new + pulse_ox_clone.component.last.valueQuantity.value = 6 + pulse_ox_clone.component.last.valueQuantity.unit = 'L/min' + pulse_ox_clone.component.last.valueQuantity.system = 'http://unitsofmeasure.org' + pulse_ox_clone.component.last.valueQuantity.code = 'L/min' + # Second component is concentration + pulse_ox_clone.component << FHIR::Observation::Component.new + pulse_ox_clone.component.last.code = create_codeable_concept('http://loinc.org','3150-0', 'Inhaled oxygen concentration') + pulse_ox_clone.component.last.valueQuantity = FHIR::Quantity.new + pulse_ox_clone.component.last.valueQuantity.value = 40 + pulse_ox_clone.component.last.valueQuantity.unit = '%' + pulse_ox_clone.component.last.valueQuantity.system = 'http://unitsofmeasure.org' + pulse_ox_clone.component.last.valueQuantity.code = '%' + # add the Pulse Oximetry as a new Bundle entry + selection_pulse_ox.entry << create_bundle_entry(pulse_ox_clone) + # add the Pulse Oximetry into the provenance + provenance.resource.target << FHIR::Reference.new + provenance.resource.target.last.reference = "urn:uuid:#{pulse_ox_clone.id}" + end + # for the second clone, data absent reason the components + pulse_ox_clone.component.each do |component| + component.valueQuantity = nil + component.dataAbsentReason = create_codeable_concept('http://terminology.hl7.org/CodeSystem/data-absent-reason', 'unknown', 'Unknown') + end + puts " - Cloned Pulse Oximetry and Added Components: #{selection_pulse_ox.entry.first.resource.id}" + end + + goal_bundle = results.find { |b| DataScript::Constraints.has(b, FHIR::Goal) } + unless goal_bundle + goal_bundle = results.find { |b| DataScript::Constraints.has(b, FHIR::Patient) } + goal = FHIR::Goal.new + goal.meta = FHIR::Meta.new + goal.meta.profile = ['http://hl7.org/fhir/us/core/StructureDefinition/us-core-goal'] + goal.id = SecureRandom.uuid + goal.lifecycleStatus = 'active' + goal.description = create_codeable_concept('http://snomed.info/sct', '281004', 'Alcoholic dementia') + goal.subject = { reference: "urn:uuid:#{DataScript::Constraints.patient(goal_bundle).id}" } + goal_target = FHIR::Goal::Target.new + goal_target.dueDate = Time.now.strftime("%Y-%m-%d") + goal.target << goal_target + goal_bundle.entry << create_bundle_entry(goal) + goal_provenance = goal_bundle.entry.find { |e| e.resource.resourceType == 'Provenance' } + goal_provenance.resource.target << FHIR::Reference.new + goal_provenance.resource.target.last.reference = "urn:uuid:#{goal.id}" + end + + observation_values_found = { + 'valueQuantity' => false, + 'valueCodeableConcept' => false, + 'valueString' => false + } + observation_profiles_valueCodeableConcept_required = [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus' + ] + observation_profiles_components_required = [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-blood-pressure' #'http://hl7.org/fhir/StructureDefinition/bp' + ] + observation_profiles = [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry', + 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-respiratory-rate', #'http://hl7.org/fhir/StructureDefinition/resprate', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-heart-rate', #'http://hl7.org/fhir/StructureDefinition/heartrate', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-temperature', #'http://hl7.org/fhir/StructureDefinition/bodytemp', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-height', #'http://hl7.org/fhir/StructureDefinition/bodyheight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-weight', #'http://hl7.org/fhir/StructureDefinition/bodyweight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-bmi', #'http://hl7.org/fhir/StructureDefinition/bmi', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-blood-pressure' #'http://hl7.org/fhir/StructureDefinition/bp' + ] + + # Add missing Head Circumference Percent resource + unless DataScript::Constraints.has_headcircum(results) + headcircum_resource = FHIR::Observation.new({ + id: SecureRandom.uuid, + meta: { + profile: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile', + 'http://hl7.org/fhir/StructureDefinition/vitalsigns' + ] + }, + category: [ + { + coding: [ + { + code: 'vital-signs', + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + display: 'Vital Signs' + } + ] + } + ], + code: { + coding: [ + { + code: '8289-1', + system: 'http://loinc.org', + display: 'Head Occipital-frontal circumference Percentile' + } + ] + }, + subject: { + reference: "urn:uuid:#{DataScript::Constraints.patient(results.first).id}" + }, + status: 'final', + effectiveDateTime: (DateTime.strptime(DataScript::Constraints.patient(results.first).birthDate, '%Y-%m-%d') + 30).iso8601, + valueQuantity: { + value: 23, + unit: '%', + system: 'http://unitsofmeasure.org', + code: '%' + } + }) + results.first.entry.push create_bundle_entry(headcircum_resource) + provenance = results.first.entry.find { |e| e.resource.is_a? FHIR::Provenance }.resource + provenance.target << FHIR::Reference.new + provenance.target.last.reference = "urn:uuid:#{headcircum_resource.id}" + end + + puts " - Checking for Observation valueQuantity, valueCodeableConcept, and valueString..." + results.each do |bundle| + entries = bundle.entry.select {|entry| entry.resource.is_a?(FHIR::Observation)} + observations = entries.map {|entry| entry.resource} + # check for valueQuantity + hasValue = observations.any? {|observation| observation.valueQuantity != nil } + observation_values_found['valueQuantity'] = true if hasValue + # check for valueCodeableConcept + hasValue = observations.any? {|observation| observation.valueCodeableConcept != nil } + observation_values_found['valueCodeableConcept'] = true if hasValue + # check for valueString + hasValue = observations.any? {|observation| observation.valueString != nil } + observation_values_found['valueString'] = true if hasValue + break if observation_values_found.values.all? { |value| value == true } + end + observation_values_found.each do |key, value| + puts " Found #{key}: #{value}" + end + + puts " - Processing Observation Data Absent Reasons" + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + provenance = bundle.entry.find { |e| e.resource.is_a? FHIR::Provenance }.resource + break if observation_profiles.empty? + observation_profiles.delete_if do |profile_url| + entry = bundle.entry.find {|e| e.resource.resourceType == 'Observation' && e.resource.meta&.profile&.include?(profile_url) } + if entry + instance = FHIR::Json.from_json(entry.resource.to_json) + instance.id = SecureRandom.uuid + instance.dataAbsentReason = create_codeable_concept('http://terminology.hl7.org/CodeSystem/data-absent-reason', 'unknown', 'Unknown') + if observation_profiles_valueCodeableConcept_required.include?(profile_url) + instance.valueCodeableConcept = instance.dataAbsentReason + instance.dataAbsentReason = nil + elsif observation_profiles_components_required.include?(profile_url) + instance.component.each do |component| + component.valueQuantity = nil + component.dataAbsentReason = instance.dataAbsentReason + end + else + instance.valueQuantity = nil + instance.valueCodeableConcept = nil + instance.valueString = nil + end + new_entry = create_bundle_entry(instance) + provenance.target << FHIR::Reference.new + provenance.target.last.reference = "urn:uuid:#{instance.id}" + bundle.entry << new_entry + puts " - #{profile_url}: #{new_entry.fullUrl}" + true # delete this profile url from the list + else + false # keep searching for this profile url in the next bundle + end + end + end + unless observation_profiles.empty? + puts " * Missed Observation Data Absent Reasons" + observation_profiles.each do |profile_url| + puts " ** #{profile_url}" + end + end + + # remove all resources from bundles that are not US Core profiles + results.each do |bundle| + bundle.entry.delete_if {|e| ['Claim','ExplanationOfBenefit','ImagingStudy','MedicationAdministration','SupplyDelivery'].include?(e.resource.resourceType)} + end + puts " - Removed resources out of scope for US Core." + # There are probably some observations remaining after this that are not US Core profiles, + # but they likely are referenced from DiagnosticReports which are US Core profiled. + + # delete provenance references to removed resources + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + provenance = bundle.entry.find {|e| e.resource.resourceType == 'Provenance' }.resource + uuids = bundle.entry.map {|e| e.fullUrl} + provenance.target.keep_if {|reference| uuids.include?(reference.reference) } + end + puts " - Rewrote Provenance targets." + + DataScript::ChoiceTypeCreator.check_choice_types(results) + + # DiagnosticReports need to have two performer types, so we add them here + puts " - Modifying DiagnosticReport performer types..." + dr_bundle = results.find do |b| + b.entry.any? do |e| + e.resource.is_a?(FHIR::DiagnosticReport) && e.resource.meta.profile.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-note') + end && + b.entry.any? do |e| + e.resource.is_a?(FHIR::DiagnosticReport) && e.resource.meta.profile.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab') + end + end + dr_notes = dr_bundle.entry.find_all do |e| + e.resource.is_a?(FHIR::DiagnosticReport) && e.resource.meta.profile.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-note') + end.map {|e| e.resource } + dr_labs = dr_bundle.entry.find_all do |e| + e.resource.is_a?(FHIR::DiagnosticReport) && e.resource.meta.profile.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab') + end.map {|e| e.resource } + practitioner_bundle = results.find {|b| b.entry.first.resource.resourceType == 'Practitioner'} + dr_practitioner = practitioner_bundle.entry.find { |e| e.resource.is_a? FHIR::Practitioner }.resource.identifier.first + organization_bundle = results.find {|b| b.entry.first.resource.resourceType == 'Organization'} + dr_organization = organization_bundle.entry.find { |e| e.resource.is_a? FHIR::Organization }.resource.identifier.first + puts " + DiagnosticReport Practitioner ID: Practitioner?identifier=#{dr_practitioner.system}|#{dr_practitioner.value}" + puts " + DiagnosticReport Organization ID: Organization?identifier=#{dr_organization.system}|#{dr_organization.value}" + dr_notes.concat(dr_labs).each do |dr| + # "performer": [ { + # "reference": "Practitioner?identifier=http://hl7.org/fhir/sid/us-npi|9999105593", + # "display": "Dr. Fernanda589 Huel628" + # } ], + dr.performer << { reference: "Practitioner?identifier=#{dr_practitioner.system}|#{dr_practitioner.value}" } + dr.performer << { reference: "Organization?identifier=#{dr_organization.system}|#{dr_organization.value}" } + end + + # The JSON from this exported patient will need to be manually altered to + # create primitive extensions, so we specifically return just this patient bundle. + selection_name + end + + def self.pick_by_gender(results) + # pick someone of the more represented gender + # in other words, if there are more males, pick a male. + # otherwise if there are more females, pick a female. + females = results.count {|b| b.entry.first.resource.resourceType == 'Patient' && b.entry.first.resource.gender == 'female'} + males = results.count {|b| b.entry.first.resource.resourceType == 'Patient' && b.entry.first.resource.gender == 'male'} + if males > females + selection = results.find {|b| b.entry.first.resource.resourceType == 'Patient' && b.entry.first.resource.gender == 'male'} + else + selection = results.find {|b| b.entry.first.resource.resourceType == 'Patient' && b.entry.first.resource.gender == 'female'} + end + selection + end + + def self.remove_name(bundle) + # Replace name with empty name. + # Technically this doesn't validate because of us-core-8: + # Patient.name.given or Patient.name.family or both SHALL be present [family.exists() or given.exists()] + # + # The JSON from this exported patient will need to be manually altered to + # create primitive extensions. + human_name = FHIR::HumanName.new + # If the us-core-8 invariant changes to allow data absent reason on name, then we can enable the next line + # human_name.extension = [ data_absent_reason ] + bundle.entry.first.resource.name = [ human_name ] + end + + def self.alter_condition(bundle, rng) + # randomly pick one of their Conditions + random_condition = bundle.entry.map {|e| e.resource }.select {|r| r.resourceType == 'Condition'}.sample(random: rng) + # and replace the category with a data-absent-reason + unknown = FHIR::CodeableConcept.new + unknown.extension = [ data_absent_reason ] + random_condition.category = [ unknown ] + random_condition + end + + def self.data_absent_reason + extension = FHIR::Extension.new + extension.url = 'http://hl7.org/fhir/StructureDefinition/data-absent-reason' + extension.valueCode = 'unknown' + extension + end + + def self.alter_smoking_status(bundle) + last_smoking_observation = bundle.entry.select {|e| e.resource.resourceType == 'Observation' && e.resource&.code&.text.start_with?('Tobacco smoking status') }.last.resource + coding = FHIR::Coding.new + coding.system = 'http://snomed.info/sct' + coding.code = '449868002' + coding.display = 'Current every day smoker' + smoker = FHIR::CodeableConcept.new + smoker.coding = [ coding ] + smoker.text = 'Current every day smoker' + last_smoking_observation.valueCodeableConcept = smoker + end + + def self.create_codeable_concept(system, code, display) + coding = FHIR::Coding.new + coding.system = system + coding.display = display + coding.code = code + codeableconcept = FHIR::CodeableConcept.new + codeableconcept.text = display + codeableconcept.coding = [ coding ] + codeableconcept + end + + def self.create_bundle_entry(resource) + entry = FHIR::Bundle::Entry.new + entry.fullUrl = "urn:uuid:#{resource.id}" + entry.resource = resource + entry.request = FHIR::Bundle::Entry::Request.new + entry.request.local_method = 'POST' + entry.request.url = resource.resourceType + entry + end + + def self.get_resource_counts(bundle) + resource_counts = bundle.entry.each_with_object({}) do |entry, rc| + resource_type = entry.resource.resourceType + if rc[resource_type] + rc[resource_type] += 1 + else + rc[resource_type] = 1 + end + end.sort + # Move DocumentReferences to the front, so we delete them first (and don't have reference issues) + resource_counts.insert(0, resource_counts.delete(resource_counts.find { |resource_name, _| resource_name == 'DocumentReference' })) + # Move Encounters to the end, so we know which ones are safe to delete + resource_counts.append(resource_counts.delete(resource_counts.find { |resource_name, _| resource_name == 'Encounter' })) + resource_counts + end + + def self.get_diagreport_referenced_observations(bundle) + bundle.entry.flat_map do |entry| + next unless entry.resource.is_a? FHIR::DiagnosticReport + entry.resource.result.map { |r| r&.reference&.split(':')&.last } + end.uniq.compact + end + + def self.get_docref_referenced_attachments(bundle) + docrefs = bundle.entry.find_all { |e| e.resource.is_a? FHIR::DocumentReference} + docrefs.map do |docref| + bundle.entry.reverse.find { |e| + e&.resource&.resourceType == 'DiagnosticReport' && + e&.resource&.presentedForm&.first&.data && + e&.resource&.presentedForm&.first&.data == docref&.resource&.content&.first&.attachment&.data }&.resource&.id + end.uniq + end + + def self.get_referenced_encounters(bundle) + bundle.entry.map do |e| + e.resource&.encounter&.reference&.split(':')&.last if e.resource.respond_to? :encounter + end.compact.uniq + end + + def self.get_referenced_reasons(bundle) + bundle.entry.flat_map do |e| + e.resource&.reasonReference&.map { |r| r.reference.split(':')&.last } if e.resource.respond_to? :reasonReference + end.compact.uniq + end + + def self.get_addresses(bundle) + bundle.entry.flat_map do |e| + e.resource&.addresses&.map { |r| r.reference.split(':')&.last } if e.resource.respond_to? :addresses + end.compact.uniq + end + + def self.get_medreqs_with_med_references(bundle) + bundle.entry.select { |e| e.resource.is_a? FHIR::MedicationRequest }.flat_map do |e| + if e.resource&.medicationReference + [e.resource.id, e&.resource&.medicationReference&.reference&.split(':')&.last] + end + end.compact.uniq + end + + def self.remove_provenance_targets(ids, provenance) + ids.each do |id| + provenance.target.delete_if { |target| target.id == id } + end + end + end +end diff --git a/lib/v5/choice_types.rb b/lib/v5/choice_types.rb new file mode 100644 index 0000000..14ee3ec --- /dev/null +++ b/lib/v5/choice_types.rb @@ -0,0 +1,54 @@ +module DataScript + # Version specific constants go here, other methods go in ../choice_type_creator.rb + class ChoiceTypeCreator + MUST_SUPPORT_CHOICE_TYPES = { + FHIR::DiagnosticReport => + { + prefix: 'effective', + suffixes: %w[DateTime Period], + profiles: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-note', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab' + ] + }, + FHIR::Immunization => + { + prefix: 'occurrence', + suffixes: %w[DateTime String], + profiles: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-immunization' + ] + }, + FHIR::Observation => + { + prefix: 'effective', + suffixes: %w[DateTime], + profiles: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-vital-signs', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-blood-pressure', #'http://hl7.org/fhir/StructureDefinition/bp' + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-bmi', #'http://hl7.org/fhir/StructureDefinition/bmi', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-head-circumference', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-height', #'http://hl7.org/fhir/StructureDefinition/bodyheight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-weight', #'http://hl7.org/fhir/StructureDefinition/bodyweight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-temperature', #'http://hl7.org/fhir/StructureDefinition/bodytemp', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-heart-rate', #'http://hl7.org/fhir/StructureDefinition/heartrate', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-bmi-for-age', + 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-weight-for-height', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-respiratory-rate' #'http://hl7.org/fhir/StructureDefinition/resprate', + # 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus' + ] + }, + FHIR::Procedure => + { + prefix: 'performed', + suffixes: %w[DateTime], + profiles: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure' + ] + } + }.freeze + end +end \ No newline at end of file diff --git a/lib/v5/constraints.rb b/lib/v5/constraints.rb new file mode 100644 index 0000000..2fa9457 --- /dev/null +++ b/lib/v5/constraints.rb @@ -0,0 +1,182 @@ +require 'date' + +module DataScript + class Constraints + + CONSTRAINTS = { + 'one_male' => lambda {|results| results.any? {|bundle| patient(bundle) && gender(bundle) == 'male'}}, + 'one_female' => lambda {|results| results.any? {|bundle| gender(bundle) == 'female'}}, + 'one_child' => lambda {|results| results.any? {|bundle| age = age(bundle); age >= 0 && age < 18 }}, + 'child_has_immunizations' => lambda {|results| results.any? {|bundle| age = age(bundle); (age >= 0 && age < 18) && has(bundle, FHIR::Immunization) }}, + 'child_does_not_smoke' => lambda {|results| results.any? {|bundle| age = age(bundle); (age >= 0 && age < 18) && !smoker(bundle) }}, + 'one_adult' => lambda {|results| results.any? {|bundle| age = age(bundle); age >= 18 && age <= 65 }}, + 'one_elder' => lambda {|results| results.any? {|bundle| age = age(bundle); age > 65 }}, + 'elder_has_device' => lambda {|results| results.any? {|bundle| age = age(bundle); (age > 65) && has(bundle, FHIR::Device) }}, + 'elder_is_alive' => lambda {|results| results.any? {|bundle| age = age(bundle); (age > 65) && alive(bundle) }}, + 'one_white' => lambda {|results| results.any? {|bundle| race(bundle) == 'White'}}, + 'one_black' => lambda {|results| results.any? {|bundle| race(bundle) == 'Black or African American'}}, + 'one_hispanic' => lambda {|results| results.any? {|bundle| ethnicity(bundle) == 'Hispanic or Latino'}}, + 'one_smoker' => lambda {|results| results.any? {|bundle| smoker(bundle) }}, + 'one_organization' => lambda {|results| results.any? {|bundle| has(bundle, FHIR::Organization) }}, + 'one_practitioner' => lambda {|results| results.any? {|bundle| has(bundle, FHIR::Practitioner) }}, + 'observation_code' => lambda {|results| results.any? {|bundle| bundle.entry.any? {|entry| entry.resource.resourceType == 'Observation' && !entry.resource.valueCodeableConcept.nil?} }}, + 'observation_quantity' => lambda {|results| results.any? {|bundle| bundle.entry.any? {|entry| entry.resource.resourceType == 'Observation' && !entry.resource.valueQuantity.nil?} }}, + 'observation_string' => lambda {|results| results.any? {|bundle| bundle.entry.any? {|entry| entry.resource.resourceType == 'Observation' && !entry.resource.valueString.nil?} }}, + 'one_imaging_study' => lambda {|results| results.any? {|bundle| has(bundle, FHIR::ImagingStudy) }} + } + + CONSTRAINTS_MRBURNS = { + 'has_allergy' => lambda {|results| results.any? {|bundle| has(bundle, FHIR::AllergyIntolerance) }}, + 'has_pulse_ox' => lambda {|results| results.any? {|bundle| has_pulse_ox(bundle) }}, + } + + CONSTRAINTS_MRBURNS_DOES_NOT_NEED = [ + 'one_female', + 'one_child', + 'child_has_immunizations', + 'child_does_not_smoke', + 'one_adult', + 'elder_is_alive', + 'one_white', + 'one_black', + 'one_hispanic', + 'one_smoker', + ] + + REQUIRED_PROFILES = [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-allergyintolerance', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-careplan', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-careteam', + #'http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition', + 'http://hl7.org/fhir/us/core//StructureDefinition/us-core-condition-encounter-diagnosis', + 'http://hl7.org/fhir/us/core//StructureDefinition/us-core-condition-problems-health-concerns', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-implantable-device', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-note', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-documentreference', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-encounter', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-goal', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-immunization', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-location', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-medication', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest', + # observations listed at end... + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-organization', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitioner', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-practitionerrole', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-procedure', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-provenance', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-questionnaireresponse', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-relatedperson', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-servicerequest', + # US Core Observation profiles... + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-clinical-test', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-imaging', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-sexual-orientation', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-social-history', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-survey', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-sdoh-assessment', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus', + #'http://hl7.org/fhir/us/core/StructureDefinition/us-core-vital-signs', # vital-signs is essentially an abstract profile... + 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-blood-pressure', #'http://hl7.org/fhir/StructureDefinition/bp' + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-bmi', #'http://hl7.org/fhir/StructureDefinition/bmi', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-height', #'http://hl7.org/fhir/StructureDefinition/bodyheight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-temperature', #'http://hl7.org/fhir/StructureDefinition/bodytemp', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-weight', #'http://hl7.org/fhir/StructureDefinition/bodyweight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-head-circumference', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-heart-rate', #'http://hl7.org/fhir/StructureDefinition/heartrate', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-bmi-for-age', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-weight-for-height', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-respiratory-rate' #'http://hl7.org/fhir/StructureDefinition/resprate', + ] + + attr_accessor :violations + + def initialize + @violations = [] + end + + def satisfied?(results, keys = CONSTRAINTS.keys) + @violations.clear + constraints = CONSTRAINTS.keep_if {|key, test| keys.include?(key)} + constraints.each do |key, test| + test_result = test.call(results) + unless test_result + @violations << key + end + end + @violations.empty? + end + + def profiles_present(results) + present = results.map {|b| b.entry.map {|e| e.resource&.meta&.profile }}.flatten.uniq + present.delete(nil) + present + end + + def self.patient(bundle) + bundle.entry.each do |entry| + return entry.resource if entry.resource.is_a?(FHIR::Patient) + end + nil + end + + def self.gender(bundle) + self.patient(bundle)&.gender + end + + def self.age(bundle) + birth_date = self.patient(bundle)&.birthDate + return -Float::INFINITY if birth_date.nil? + date = Date.parse(birth_date) + age = Date.today.year - date.year + age -= 1 if Date.today < date.next_year(age) + age + end + + def self.alive(bundle) + p = self.patient(bundle) + ((p&.deceasedBoolean.nil? || p&.deceasedBoolean == false) && p&.deceasedDateTime.nil?) + end + + def self.race(bundle) + self.patient(bundle)&.us_core_race&.ombCategory&.display rescue nil + end + + def self.ethnicity(bundle) + self.patient(bundle)&.us_core_ethnicity&.ombCategory&.display rescue nil + end + + def self.smoker(bundle) + entries = bundle.entry.select {|entry| entry.resource.is_a?(FHIR::Observation)} + observations = entries.map {|entry| entry.resource} + smoking_statuses = observations.select {|observation| observation.code.text&.start_with?('Tobacco smoking status')} + altA = smoking_statuses.map {|status| status.value.text}.include? 'Current every day smoker' + altB = smoking_statuses.map {|status| status.value.text}.include? 'Smokes tobacco daily (finding)' + (altA || altB) + end + + def self.has(bundle, fhir_class) + bundle.entry.any? {|entry| entry.resource.is_a?(fhir_class)} + end + + def self.has_pulse_ox(bundle) + bundle.entry.any? do |entry| + entry.resource&.meta&.profile&.include? 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry' + end + end + + def self.has_headcircum(results) + results.any? do |bundle| + bundle.entry.any? do |entry| + entry.resource&.meta&.profile&.include? 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile' + end + end + end + end +end diff --git a/lib/v5/modifications.rb b/lib/v5/modifications.rb new file mode 100644 index 0000000..ab96844 --- /dev/null +++ b/lib/v5/modifications.rb @@ -0,0 +1,1212 @@ +# frozen_string_literal: true + +require 'base64' +require 'securerandom' +require_relative 'constraints' +require_relative '../choice_type_creator' +require 'fhir_models' +require 'time' + +module DataScript + class Modifications + DESIRED_MAX = 20 + # SOCIAL HISTORY are SDOH related, but not captured in SURVEYS like PRAPARE + # These are SNOMED codes + SDOH_CONDITIONS = ['105531004','10939881000119100','160701002','160903007','160904001','160968000','224295006','224299000','224355006','266934004','266948004','32911000','361055000','422650009','423315002','424393004','446654005','5251000175109','706893006','713458007','73438004','73595000','741062008'] + CLINICAL_TEST_OBSERVATIONS = ['44963-7'] + + # SURVEY is for surveys and screenings that are NOT SDOH related + SURVEY_OBSERVATIONS = ['44249-1','44261-6','55757-9','55758-7','59453-1','59460-6','59461-4','61576-5','62337-1','69737-5','70274-6','71933-6','71934-4','71956-7','71958-3','71960-9','71962-5','71964-1','71966-6','71968-2','71970-8','71972-4','71973-2','71974-0','71975-7','71976-5','71977-3','71978-1','71979-9','71980-7','72009-4','72010-2','72011-0','72012-8','72013-6','72014-4','72015-1','72016-9','72091-2','72092-0','72093-8','72094-6','72095-3','72096-1','72097-9','72098-7','72099-5','72100-1','72101-9','72102-7','72109-2','75626-2','76499-3','76504-0','82666-9','82667-7','89204-2','89206-7'] + # SDOH ASSESSMENTS include thiings like PRAPARE and other HRSN screenings. + # There are 47 specific codes in the ValueSet expansion... + SDOH_ASSESSMENT_OBSERVATIONS = ['93028-9','93025-5','69861-3','93027-1','81375-8','93034-7','68516-4','96782-8','93029-7','68517-2','96842-0','88123-5','76501-6','95618-5','93038-8','69858-9','93159-2','96780-2','44250-9','68524-8','88121-9','93026-3','82589-3','89555-7','96781-0','95530-2','56799-0','63586-2','96779-4','93677-3','63512-8','76437-3','88124-3','32624-9','97023-6','93035-4','54899-0','93033-9','93031-3','56051-6','88122-7','93030-5','97027-7','71802-3','44255-8','67875-5','76513-1'] + QUESTIONNAIRE_PRAPARE = 'http://hl7.org/fhir/us/sdoh-clinicalcare/Questionnaire/SDOHCC-QuestionnairePRAPARE' + + def self.modify!(results, random_seed = 3) + FHIR.logger.level = :info + + # Create a random number generator, to pass to things that need randomness + rng = Random.new(random_seed) + # results is an Array of FHIR::Bundle objects, + # where the first resource is a Patient. + + # Remove unwanted patient extensions and identifers + results.each do |bundle| + if bundle.entry.first.resource.resourceType == 'Patient' + # first, remove unwanted patient extensions + bundle.entry.first.resource.extension.delete_if do |extension| + extension.url.start_with? 'http://synthetichealth.github.io' + end + + # next, remove unwanted patient identifier + bundle.entry.first.resource.identifier.delete_if do |identifier| + identifier.system.start_with? 'http://standardhealthrecord.org' + end + + # make sure every patient has a postalCode + # some towns do not have postalCodes + bundle.entry.first.resource.address.first.postalCode = '01999' unless bundle.entry.first.resource.address.first.postalCode + + # finally, make sure every patient has an address period + bundle.entry.first.resource.address.first.period = FHIR::Period.new + bundle.entry.first.resource.address.first.period.start = bundle.entry.first.resource.birthDate + else + # Not a patient bundle, probably contains Organizations, Locations, Practitioners, and/or PractitionerRoles + # Remove unwanted extensions + bundle.entry.each do |entry| + entry.resource.extension.delete_if { |extension| extension.url.start_with? 'http://synthetichealth.github.io' } + end + end + end + + # Make sure there aren't stupid numbers of every resource type + # missing_profiles = DataScript::Constraints::REQUIRED_PROFILES.dup + # puts ' - Removing resources...' + # all_deleted_ids = [] + # results.each do |bundle| + # resource_counts = get_resource_counts(bundle) + # provenance = bundle.entry.find { |e| e.resource.is_a? FHIR::Provenance }&.resource + # next unless provenance + # resource_counts.each do |type, count| + # next unless count >= DESIRED_MAX + + # deleted_ids = [] + # medication_refs = get_medreqs_with_med_references(bundle) + # references = (dr_observations + dr_notes + encounter_refs + reason_refs + addresses_refs + medication_refs).compact.uniq + # bundle.entry.find_all { |e| e.resource.resourceType == type }.shuffle(random: rng).each do |e| + # break if deleted_ids.count >= (count - DESIRED_MAX) + + # profiles = e.resource&.meta&.profile || [] + + # # Only delete it if it's not somehow important + # if !references.include?(e.resource.id) && + # !(e.resource.is_a?(FHIR::Observation) && e.resource&.code&.text.start_with?('Tobacco smoking status')) && + # (missing_profiles & profiles).empty? + # deleted_ids << e.resource.id + # elsif !(missing_profiles & profiles).empty? + # missing_profiles -= profiles + # end + # end + # bundle.entry.delete_if { |e| deleted_ids.include? e.resource.id } + # remove_provenance_targets(deleted_ids, provenance) + # all_deleted_ids.append(deleted_ids) + # end + # end + # puts " - Removed #{all_deleted_ids.flatten.uniq.count} resources from #{results.length} Bundles." + + # add reaction.manifestation to allergy intolerance result because of must support changes in 3.1.1 + already_contains_reaction_manifestation = + results.any? do |bundle| + bundle + .entry + .select { |e| e.resource.is_a? FHIR::AllergyIntolerance } + .map(&:resource) + .any? do |resource| + resource.reaction.any? { |reaction| reaction.manifestation.any? } + end + end + unless already_contains_reaction_manifestation + results.each do |bundle| + allergy_intoleranace_resource = bundle.entry.find { |e| e.resource.is_a? FHIR::AllergyIntolerance }&.resource + next if allergy_intoleranace_resource.nil? + + reaction = FHIR::AllergyIntolerance::Reaction.new + manifestation = create_codeable_concept('http://snomed.info/sct', '271807003', 'Eruption of skin (disorder)') + reaction.manifestation << manifestation + allergy_intoleranace_resource.reaction << reaction + puts " - Altered AllergyIntolerance: #{allergy_intoleranace_resource.id}" + break + end + end + + # Add a ServiceRequest for each CarePlan + service_requests_for_careplans = 0 + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + provenance = bundle.entry.find { |e| e.resource.is_a? FHIR::Provenance }.resource + bundle.entry.each do |entry| + next unless entry.resource.resourceType == 'CarePlan' + # Add a ServiceRequest + encounter = get_resource_by_id(bundle, entry.resource.encounter.reference) + service_request = FHIR::ServiceRequest.new({ + id: SecureRandom.uuid, + meta: { profile: [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-servicerequest' ] }, + category: [{ coding: [{ + system: 'http://snomed.info/sct', + code: '409073007', + display: 'Education' + }]}], + code: entry.resource.category.last, + subject: entry.resource.subject, + encounter: entry.resource.encounter, + requester: encounter&.participant&.first&.individual, + status: entry.resource.status, + intent: 'order', + occurrencePeriod: entry.resource.period, + authoredOn: entry.resource.period.start + }) + service_request_reference = FHIR::Reference.new + service_request_reference.reference = "urn:uuid:#{service_request.id}" + provenance.target << service_request_reference + bundle.entry << create_bundle_entry(service_request) + service_requests_for_careplans += 1 + end + end + if service_requests_for_careplans > 0 + puts (" - Generated #{service_requests_for_careplans} service requests for CarePlans.") + else + error(" * Unable to find a CarePlan to make an service request.") + end + + # Add a RelatedPerson to each CareTeam + related_person_for_careteam = 0 + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + provenance = bundle.entry.find { |e| e.resource.is_a? FHIR::Provenance }.resource + careteam = bundle.entry.find { |e| e.resource.is_a? FHIR::CareTeam }&.resource + next if careteam.nil? + # Add a RelatedPerson + related_person = FHIR::RelatedPerson.new({ + id: SecureRandom.uuid, + meta: { profile: [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-relatedperson' ] }, + active: (careteam.status == 'active'), + patient: careteam.subject, + relationship: [{ coding: [{ + system: 'http://terminology.hl7.org/CodeSystem/v3-RoleCode', + code: 'ROOM', + display: 'Roommate' + }]}], + name: [{ + use: 'official', + family: 'Jefferson174', + given: [ 'Ronald408', 'MacGyver246' ], + prefix: [ 'Mr.' ] + }], + telecom: bundle.entry.first.resource.telecom, + address: bundle.entry.first.resource.address + }) + related_person_reference = FHIR::Reference.new + related_person_reference.reference = "urn:uuid:#{related_person.id}" + provenance.target << related_person_reference + bundle.entry << create_bundle_entry(related_person) + careteam.participant << FHIR::CareTeam::Participant.new({ + role: [ { + coding: [ { + system: 'http://snomed.info/sct', + code: '133932002', + display: 'Caregiver (person)' + } ], + text: 'Caregiver (person)' + } ], + member: related_person_reference + }) + related_person_for_careteam += 1 + end + if related_person_for_careteam > 0 + puts (" - Generated #{related_person_for_careteam} RelatedPerson for CareTeams.") + else + error(" * Unable to find a CareTeam to add a RelatedPerson into.") + end + + # Add discharge disposition to every encounter referenced by a medicationRequest of each record + # This is necessary (rather than just one) because of how Inferno Program has to get Encounters + results.each do |bundle| + encounter_urls = bundle.entry.find_all { |e| e.resource.resourceType == 'MedicationRequest' }.map { |e| e.resource&.encounter&.reference }.compact.uniq + encounter_urls.each do |encounter_url| + encounter_entry = bundle.entry.find { |e| e.fullUrl == encounter_url } + encounter = encounter_entry.resource + encounter.hospitalization = FHIR::Encounter::Hospitalization.new + encounter.hospitalization.dischargeDisposition = create_codeable_concept('http://www.nubc.org/patient-discharge','01','Discharged to home care or self care (routine discharge)') + end + end + + # Make sure at least one organization has an NPI + result = results.find {|b| DataScript::Constraints.has(b, FHIR::Organization)} + organization_entry = result.entry.find { |e| e.resource.resourceType == 'Organization' } + organization = organization_entry.resource + # Add a 10 digit NPI + organization.identifier << FHIR::Identifier.new + organization.identifier.last.system = 'http://hl7.org/fhir/sid/us-npi' + organization.identifier.last.value = '9999999999' + # Add a CLIA + organization.identifier << FHIR::Identifier.new + organization.identifier.last.system = 'urn:oid:2.16.840.1.113883.4.7' + organization.identifier.last.value = '9999999999' + + # Add a PractitionerRole.endpoint + result = results.find {|b| DataScript::Constraints.has(b, FHIR::PractitionerRole)} + pr_entry = result.entry.find { |e| e.resource.resourceType == 'PractitionerRole' } + pr = pr_entry.resource + pr.endpoint = [ FHIR::Reference.new ] + pr.endpoint.first.reference = '#endpoint' + pr.endpoint.first.type = 'Endpoint' + endpoint = FHIR::Endpoint.new + endpoint.id = 'endpoint' + endpoint.status = 'active' + endpoint.connectionType = create_codeable_concept('http://terminology.hl7.org/CodeSystem/endpoint-connection-type', 'direct-project', 'Direct Project').coding.first + endpoint.payloadType = [ create_codeable_concept('http://terminology.hl7.org/CodeSystem/endpoint-payload-type', 'any', 'Any') ] + endpoint.address = "mailto:#{pr.telecom.last.value}" + pr.contained = [endpoint] + + # select by smoking status + selection_smoker = results.find {|b| DataScript::Constraints.smoker(b)} + unless selection_smoker + # if there is no smoker, choose the oldest patient + # and make them start smoking at the end of their life... + # because why not? + patient_results = results.find_all {|b| b.entry.first.resource.resourceType == 'Patient'} + oldest = patient_results.sort {|a,b| a.entry.first.resource.birthDate <=> b.entry.first.resource.birthDate }.first + alter_smoking_status(oldest) + puts " - Altered Smoker: #{oldest.entry.first.resource.id}" + end + + # sort the results by number of Conditions + results.sort! do |a,b| + count_a = a.entry.count {|e| e.resource.resourceType == 'Condition'} + count_b = b.entry.count {|e| e.resource.resourceType == 'Condition'} + count_a <=> count_b + end + # select the person with the most Conditions + selection_conditions = results.last + altered = alter_condition(selection_conditions, rng) + puts " - Altered Condition: #{altered.id}" + + puts ' - Rewriting Condition Profiles for v5...' + social_history_obs_inserted = 0 + this_or_that = true + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + provenance = bundle.entry.find { |e| e.resource.is_a? FHIR::Provenance }.resource + bundle.entry.each do |entry| + next unless entry.resource.resourceType == 'Condition' + category = entry.resource.category&.first&.coding&.first&.code + if SDOH_CONDITIONS.include?(entry.resource.code&.coding&.first&.code) + entry.resource.meta.profile[0] = 'http://hl7.org/fhir/us/core//StructureDefinition/us-core-condition-problems-health-concerns' + entry.resource.category = [] + if this_or_that + entry.resource.category << create_codeable_concept('http://terminology.hl7.org/CodeSystem/condition-category', 'problem-list-item', 'Problem List Item') + else + entry.resource.category << create_codeable_concept('http://hl7.org/fhir/us/core/CodeSystem/condition-category', 'health-concern', 'Health Concern') + end + this_or_that = !this_or_that + entry.resource.category << create_codeable_concept('http://hl7.org/fhir/us/core/CodeSystem/us-core-tags', 'sdoh', 'SDOH') + + # Also make an social-history observation for this finding... + social_history_obs = FHIR::Observation.new({ + id: SecureRandom.uuid, + meta: { profile: [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-social-history' ] }, + category: [{ coding: [{ + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'social-history', + display: 'Social History' + }]},{ coding: [{ + system: 'http://hl7.org/fhir/us/core/CodeSystem/us-core-tags', + code: 'sdoh', + display: 'SDOH' + }]}], + code: entry.resource.code, + subject: entry.resource.subject, + encounter: entry.resource.encounter, + status: 'final', + effectiveDateTime: entry.resource.onsetDateTime, + valueBoolean: true + }) + social_history_obs_reference = FHIR::Reference.new + social_history_obs_reference.reference = "urn:uuid:#{social_history_obs.id}" + provenance.target << social_history_obs_reference + bundle.entry << create_bundle_entry(social_history_obs) + social_history_obs_inserted += 1 + elsif category == 'encounter-diagnosis' + entry.resource.meta.profile[0] = 'http://hl7.org/fhir/us/core//StructureDefinition/us-core-condition-encounter-diagnosis' + end + entry.resource.extension << asserted_date(entry.resource.recordedDate) + end + end + puts (" - Added #{social_history_obs_inserted} social-history observations based on SNOMED codes.") + + # select someone with the most numerous gender + # from the people remaining, and remove their name + unless MRBURNS + selection_name = pick_by_gender(results) + remove_name(selection_name) + puts " - Altered Name: #{selection_name.entry.first.resource.id}" + end + + # select by clinical note + selection_note = results.find {|b| DataScript::Constraints.has(b, FHIR::DocumentReference)} + if selection_note + # modify it to have a URL rather than base64 encoded data + docref = selection_note.entry.reverse.find { |e| e.resource.resourceType == 'DocumentReference' }.resource + report = selection_note.entry.reverse.find { |e| + e&.resource&.resourceType == 'DiagnosticReport' && + e&.resource&.presentedForm&.first&.data == docref&.content&.first&.attachment&.data }&.resource + url = 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf' + docref.content.first.attachment.contentType = 'application/pdf' + docref.content.first.attachment.data = nil + docref.content.first.attachment.url = url + report.presentedForm.first.contentType = 'application/pdf' + report.presentedForm.first.data = nil + report.presentedForm.first.url = url + puts " - Altered DocumentReference URL: #{docref.id}" + puts " - Altered DiagnosticReport URL: #{report.id}" + else + error(' * FAILED to find DocumentReference!') + end + + # collect all the clinical notes and modify codes so we have at least one of each type + category_types = [ + [ 'Cardiology', 'LP29708-2' ], + [ 'Pathology', 'LP7839-6' ], + [ 'Radiology', 'LP29684-5' ] + ] + note_types = [ + [ 'Consult note', '11488-4' ], + [ 'Discharge summary', '18842-5' ], + [ 'History and physical note', '34117-2' ], + [ 'Procedure note', '28570-0' ], + [ 'Progress note', '11506-3' ], + [ 'Diagnostic imaging study', '18748-4' ], + [ 'Laboratory report', '11502-2' ], + [ 'Pathology study', '11526-1' ], + [ 'Referral note', '57133-1' ], + [ 'Surgical operation note', '11504-8' ], + [ 'Nurse Note', '34746-8' ] + ] + category_types.map! {|type| create_codeable_concept('http://loinc.org',type.last,type.first) } + note_types.map! {|type| create_codeable_concept('http://loinc.org',type.last,type.first) } + # grab all the clinical notes + all_docref = results.map {|b| b.entry.select {|e| e.resource.resourceType == 'DocumentReference'}.map {|e| e.resource} }.flatten + all_report = results.map {|b| b.entry.select {|e| e.resource.resourceType == 'DiagnosticReport'}.map {|e| e.resource} }.flatten + # there are more DiagnosticReports than DocumentReferences, + # so we need to filter them... + docref_data = all_docref.map {|r| r.content.first.attachment.data} + matching_report = all_report.select { |r| r.presentedForm.length >= 1 && docref_data.include?(r.presentedForm.first.data) } + + # need to replace the codes... + # we will use a uniform distribution of note_types + note_types_index = 0 + category_types_index = 0 + all_docref.zip(matching_report).each do |docref, report| + break if report.nil? + docref.type = note_types[note_types_index] + report.category = [ category_types[category_types_index] ] + report.code = note_types[note_types_index] + note_types_index += 1 + category_types_index += 1 + note_types_index = 0 if note_types_index >= note_types.length + category_types_index = 0 if category_types_index >= category_types.length + end + puts " - Altered codes for #{all_docref.length} clinical notes." + + all_docref.each do |docref| + docref&.identifier&.each {|id| id.value = "urn:uuid:#{id.value}" if (id.system == 'urn:ietf:rfc:3986' && !id.value&.start_with?('urn'))} + end + puts " - Altered identifiers for #{all_docref.length} clinical notes." + + # select by medication + selection_medication = results.find {|b| DataScript::Constraints.has(b, FHIR::Medication)} + unless selection_medication + # if there is no free-standing Medication resource, we need to make one. + med_bundle = results.find { |b| DataScript::Constraints.has(b, FHIR::MedicationRequest) } + medreq = med_bundle.entry.find { |e| e.resource.resourceType == 'MedicationRequest' } + provenance = med_bundle.entry.find { |e| e.resource.resourceType == 'Provenance' } + # create the medication + med = FHIR::Medication.new + med.id = SecureRandom.uuid + med.status = 'active' + med.meta = FHIR::Meta.new + med.meta.profile = [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-medication' ] + med.code = medreq.resource.medicationCodeableConcept + # alter the MedicationRequest to refer to the Medication resource and not a code + medreq.resource.reportedBoolean = true + medreq.resource.medicationCodeableConcept = nil + medreq.resource.medicationReference = FHIR::Reference.new + medreq.resource.medicationReference.reference = "urn:uuid:#{med.id}" + medreq.resource.status = 'active' + # add the Medication as a new Bundle entry + med_bundle.entry << create_bundle_entry(med) + # add the Medication into the provenance + provenance.resource.target << FHIR::Reference.new + provenance.resource.target.last.reference = "urn:uuid:#{med.id}" + puts " - Altered Medication: #{med_bundle.entry.first.resource.id}" + end + + # change one medication request from an order to a self prescription, if needed + # we need at least two intents in order to demonstrate the multi-or search requirement for intent + any_non_order = results.any? {|b| b.entry.any?{|e| e.resource.resourceType == 'MedicationRequest' && e.resource.intent != 'order' }} + selection_medication_request = results.find {|b| DataScript::Constraints.has(b, FHIR::MedicationRequest)} + if !any_non_order && selection_medication_request + changed_medication = selection_medication_request.entry.select { |e| e.resource.resourceType == 'MedicationRequest'}.last.resource + changed_medication.intent = 'plan' + changed_medication.reportedBoolean = true + changed_medication.reportedReference = nil + changed_medication.requester = changed_medication.subject.clone + changed_medication.encounter = nil + puts " - Altered Medication Request to have 'plan' intent: #{changed_medication.id}" + end + + # select by device + selection_device = results.find {|b| DataScript::Constraints.has(b, FHIR::Device)} + if selection_device + # if there is a Device resource, we need to clone it and use carrierAIDC. + device = selection_device.entry.find { |e| e.resource.resourceType == 'Device' } + provenance = selection_device.entry.find { |e| e.resource.resourceType == 'Provenance' } + # create the new Device + dev = FHIR::Device.new(device.resource.to_hash) + dev.id = SecureRandom.uuid + dev.udiCarrier.first.carrierHRF = nil + barcode_file_path = File.join(File.dirname(__FILE__), '../barcode.png') + barcode_file = File.open(barcode_file_path, 'rb') + barcode_data = barcode_file.read + barcode_file.close + dev.udiCarrier.first.carrierAIDC = Base64.encode64(barcode_data) + # add the Device as a new Bundle entry + selection_device.entry << create_bundle_entry(dev) + # add the Device into the provenance + provenance.resource.target << FHIR::Reference.new + provenance.resource.target.last.reference = "urn:uuid:#{dev.id}" + puts " - Cloned Device: #{selection_device.entry.first.resource.id}" + end + + # select an Immunization + selection_immunization = results.find {|b| DataScript::Constraints.has(b, FHIR::Immunization)} + if selection_immunization + # if there is an Immunization resource, we need to clone it and use carrierAIDC. + immunization_entry = selection_immunization.entry.find { |e| e.resource.resourceType == 'Immunization' } + immunization = immunization_entry.resource + immunization.vaccineCode = create_codeable_concept('http://terminology.hl7.org/CodeSystem/data-absent-reason', 'unknown', 'Unknown') + immunization.statusReason = create_codeable_concept('http://terminology.hl7.org/CodeSystem/v3-ActReason', 'OSTOCK', 'product out of stock') + immunization.status = 'not-done' + puts " - Altered Immunization: #{selection_immunization.entry.first.resource.id}" + end + + # select Bundle with Pulse Oximetry + selection_pulse_ox = results.find {|b| DataScript::Constraints.has_pulse_ox(b)} + if selection_pulse_ox + provenance = selection_pulse_ox.entry.find { |e| e.resource.resourceType == 'Provenance' } + pulse_ox_entry = selection_pulse_ox.entry.find {|e| e.resource&.meta&.profile&.include? 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry' } + pulse_ox_clone = nil + 2.times do + pulse_ox_clone = FHIR.from_contents(pulse_ox_entry.resource.to_json) + pulse_ox_clone.id = SecureRandom.uuid + # Add the must support components + pulse_ox_clone.component = [] + # First component is flow rate + pulse_ox_clone.component << FHIR::Observation::Component.new + pulse_ox_clone.component.last.code = create_codeable_concept('http://loinc.org','3151-8', 'Inhaled oxygen flow rate') + pulse_ox_clone.component.last.valueQuantity = FHIR::Quantity.new + pulse_ox_clone.component.last.valueQuantity.value = 6 + pulse_ox_clone.component.last.valueQuantity.unit = 'L/min' + pulse_ox_clone.component.last.valueQuantity.system = 'http://unitsofmeasure.org' + pulse_ox_clone.component.last.valueQuantity.code = 'L/min' + # Second component is concentration + pulse_ox_clone.component << FHIR::Observation::Component.new + pulse_ox_clone.component.last.code = create_codeable_concept('http://loinc.org','3150-0', 'Inhaled oxygen concentration') + pulse_ox_clone.component.last.valueQuantity = FHIR::Quantity.new + pulse_ox_clone.component.last.valueQuantity.value = 40 + pulse_ox_clone.component.last.valueQuantity.unit = '%' + pulse_ox_clone.component.last.valueQuantity.system = 'http://unitsofmeasure.org' + pulse_ox_clone.component.last.valueQuantity.code = '%' + # add the Pulse Oximetry as a new Bundle entry + selection_pulse_ox.entry << create_bundle_entry(pulse_ox_clone) + # add the Pulse Oximetry into the provenance + provenance.resource.target << FHIR::Reference.new + provenance.resource.target.last.reference = "urn:uuid:#{pulse_ox_clone.id}" + end + # for the second clone, data absent reason the components + pulse_ox_clone.component.each do |component| + component.valueQuantity = nil + component.dataAbsentReason = create_codeable_concept('http://terminology.hl7.org/CodeSystem/data-absent-reason', 'unknown', 'Unknown') + end + puts " - Cloned Pulse Oximetry and Added Components: #{selection_pulse_ox.entry.first.resource.id}" + end + + goal_bundle = results.find { |b| DataScript::Constraints.has(b, FHIR::Goal) } + unless goal_bundle + goal_bundle = results.find { |b| DataScript::Constraints.has(b, FHIR::Patient) } + goal = FHIR::Goal.new + goal.meta = FHIR::Meta.new + goal.meta.profile = ['http://hl7.org/fhir/us/core/StructureDefinition/us-core-goal'] + goal.id = SecureRandom.uuid + goal.lifecycleStatus = 'active' + goal.description = create_codeable_concept('http://snomed.info/sct', '281004', 'Dementia associated with alcoholism (disorder)') + goal.subject = { reference: "urn:uuid:#{DataScript::Constraints.patient(goal_bundle).id}" } + goal_target = FHIR::Goal::Target.new + goal_target.dueDate = Time.now.strftime("%Y-%m-%d") + goal.target << goal_target + goal_bundle.entry << create_bundle_entry(goal) + goal_provenance = goal_bundle.entry.find { |e| e.resource.resourceType == 'Provenance' } + goal_provenance.resource.target << FHIR::Reference.new + goal_provenance.resource.target.last.reference = "urn:uuid:#{goal.id}" + end + + observation_values_found = { + 'valueQuantity' => false, + 'valueCodeableConcept' => false, + 'valueString' => false + } + observation_profiles_valueCodeableConcept_required = [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-smokingstatus' + ] + observation_profiles_components_required = [ + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-blood-pressure' #'http://hl7.org/fhir/StructureDefinition/bp' + ] + observation_profiles = [ + #'http://hl7.org/fhir/us/core/StructureDefinition/us-core-vital-signs', # vital-signs is more or less an abstract profile + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-bmi', #'http://hl7.org/fhir/StructureDefinition/bmi', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-head-circumference', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-height', #'http://hl7.org/fhir/StructureDefinition/bodyheight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-weight', #'http://hl7.org/fhir/StructureDefinition/bodyweight', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-body-temperature', #'http://hl7.org/fhir/StructureDefinition/bodytemp', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-heart-rate', #'http://hl7.org/fhir/StructureDefinition/heartrate', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-bmi-for-age', + 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile', + 'http://hl7.org/fhir/us/core/StructureDefinition/pediatric-weight-for-height', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-pulse-oximetry', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-respiratory-rate', #'http://hl7.org/fhir/StructureDefinition/resprate', + 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-imaging' + ] + # Add missing Head Circumference Percent resource + unless DataScript::Constraints.has_headcircum(results) + headcircum_resource = FHIR::Observation.new({ + id: SecureRandom.uuid, + meta: { + profile: [ + 'http://hl7.org/fhir/us/core/StructureDefinition/head-occipital-frontal-circumference-percentile', + 'http://hl7.org/fhir/StructureDefinition/vitalsigns' + ] + }, + category: [ + { + coding: [ + { + code: 'vital-signs', + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + display: 'Vital Signs' + } + ] + } + ], + code: { + coding: [ + { + code: '8289-1', + system: 'http://loinc.org', + display: 'Head Occipital-frontal circumference Percentile' + } + ] + }, + subject: { + reference: "urn:uuid:#{DataScript::Constraints.patient(results.first).id}" + }, + status: 'final', + effectiveDateTime: (DateTime.strptime(DataScript::Constraints.patient(results.first).birthDate, '%Y-%m-%d') + 30).iso8601, + valueQuantity: { + value: 23, + unit: '%', + system: 'http://unitsofmeasure.org', + code: '%' + } + }) + results.first.entry.push create_bundle_entry(headcircum_resource) + provenance = results.first.entry.find { |e| e.resource.is_a? FHIR::Provenance }.resource + provenance.target << FHIR::Reference.new + provenance.target.last.reference = "urn:uuid:#{headcircum_resource.id}" + end + + puts ' - Adding Observation Imaging Results and ServiceRequests for each ImagingStudy...' + imaging_study_codes = [ + ['18782-3','Radiology Study observation (narrative)'], + ['19005-8','Radiology Imaging study [Impression] (narrative)'], + ['18834-2','Radiology Comparison study (narrative)'] + ] + imaging_study_obs_added = 0 + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + provenance = bundle.entry.find { |e| e.resource.is_a? FHIR::Provenance }.resource + bundle.entry.each do |entry| + next unless entry.resource.resourceType == 'ImagingStudy' + # Add an Observation Imaging Result + code = imaging_study_codes.sample + imaging_study_obs = FHIR::Observation.new({ + id: SecureRandom.uuid, + meta: { profile: [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-imaging' ] }, + category: [{ coding: [{ + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'imaging', + display: 'Imaging' + }]}], + code: { coding: [{ + system: 'http://loinc.org', + code: code.first, + display: code.last + }]}, + subject: entry.resource.subject, + encounter: entry.resource.encounter, + status: 'final', + effectiveDateTime: entry.resource.started, + valueString: "#{entry.resource.procedureCode.first.text} results: abnormal" + }) + imaging_study_obs_reference = FHIR::Reference.new + imaging_study_obs_reference.reference = "urn:uuid:#{imaging_study_obs.id}" + provenance.target << imaging_study_obs_reference + bundle.entry << create_bundle_entry(imaging_study_obs) + imaging_study_obs_added += 1 + + # Add a ServiceRequest + encounter = get_resource_by_id(bundle, entry.resource.encounter.reference) + service_request = FHIR::ServiceRequest.new({ + id: SecureRandom.uuid, + meta: { profile: [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-servicerequest' ] }, + category: [{ coding: [{ + system: 'http://snomed.info/sct', + code: '363679005', + display: 'Imaging' + }]}], + code: entry.resource.procedureCode.first, + subject: entry.resource.subject, + encounter: entry.resource.encounter, + requester: encounter&.participant&.first&.individual, + status: 'completed', + intent: 'order', + occurrenceDateTime: entry.resource.started, + authoredOn: entry.resource.started + }) + service_request_reference = FHIR::Reference.new + service_request_reference.reference = "urn:uuid:#{service_request.id}" + provenance.target << service_request_reference + bundle.entry << create_bundle_entry(service_request) + end + end + if imaging_study_obs_added > 0 + puts (" - Generated #{imaging_study_obs_added} each: observation imaging results, and service requests.") + else + error(" * Unable to find an ImagingStudy to make an observation imaging result.") + end + + puts ' - Relabeling Clinical Test Observations...' + relabeled_clinical_test_obs = 0 + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + bundle.entry.each do |entry| + next unless entry.resource.resourceType == 'Observation' + if CLINICAL_TEST_OBSERVATIONS.include?(entry.resource.code.coding.first.code) + entry.resource.meta = FHIR::Meta.new + entry.resource.meta.profile = ['http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-clinical-test'] + entry.resource.category = [ create_codeable_concept('http://hl7.org/fhir/us/core/CodeSystem/us-core-observation-category', 'clinical-test', 'Clinical Test') ] + relabeled_clinical_test_obs += 1 + end + end + end + if relabeled_clinical_test_obs > 0 + puts (" - Relabeled #{relabeled_clinical_test_obs} clinical test observations.") + else + puts (" * Unable to find an clinical test observation to relabel (this is not an error).") + end + + puts ' - Generating Clinical Test Observations for some Procedures...' + generated_clinical_test_obs = 0 + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + provenance = bundle.entry.find { |e| e.resource.is_a? FHIR::Provenance }.resource + last_encounter = bundle.entry.reverse.find { |e| e.resource.is_a? FHIR::Encounter }.resource + walk_test_obs = FHIR::Observation.new({ + id: SecureRandom.uuid, + meta: { profile: [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-clinical-test' ] }, + category: [{ coding: [{ + system: 'http://hl7.org/fhir/us/core/CodeSystem/us-core-observation-category', + code: 'clinical-test', + display: 'Clinical Test' + }]}], + code: { coding: [{ + system: 'http://loinc.org', + code: '64098-7', + display: 'Six minute walk test' + }]}, + subject: last_encounter.subject, + encounter: { reference: "urn:uuid:#{last_encounter.id}" }, + status: 'final', + effectivePeriod: last_encounter.period, + valueQuantity: { + value: ((300 * rand) + 400).to_i, + unit: 'm/(6.min)', + system: 'http://unitsofmeasure.org', + code: 'm/(6.min)' + } + }) + walk_test_obs_reference = FHIR::Reference.new + walk_test_obs_reference.reference = "urn:uuid:#{walk_test_obs.id}" + provenance.target << walk_test_obs_reference + bundle.entry << create_bundle_entry(walk_test_obs) + generated_clinical_test_obs += 1 + end + if generated_clinical_test_obs > 0 + puts (" - Generated #{generated_clinical_test_obs} clinical test observations.") + else + error(" * Unable to find an Encounter to add a walk test onto.") + end + + sexual_orientation_codes = [ + create_codeable_concept('http://snomed.info/sct', '38628009', 'Homosexuality'), + create_codeable_concept('http://snomed.info/sct', '20430005', 'Heterosexual state'), + create_codeable_concept('http://snomed.info/sct', '42035005', 'Bisexual state'), + create_codeable_concept('http://terminology.hl7.org/CodeSystem/v3-NullFlavor', 'OTH', 'Other'), + create_codeable_concept('http://terminology.hl7.org/CodeSystem/v3-NullFlavor', 'UNK', 'Unknown'), + create_codeable_concept('http://terminology.hl7.org/CodeSystem/v3-NullFlavor', 'ASKU', 'Asked but no answer') + ] + puts ' - Preprocessing PRAPARE Observations...' + prapare_count = 0 + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + provenance = bundle.entry.find { |e| e.resource.is_a? FHIR::Provenance }.resource + sexual_orientation = sexual_orientation_codes.sample + bundle.entry.each do |entry| + next unless entry.resource.resourceType == 'Observation' + code = entry.resource.code&.coding&.first&.code + if code == '93025-5' # PRAPARE Multi-Observation + # Create a QuestionnaireResponse + questionnaireResponse, serviceRequest = create_questionnaire_response_from_multiobservation(entry.resource) + questionnaireResponseReference = FHIR::Reference.new + questionnaireResponseReference.reference = "urn:uuid:#{questionnaireResponse.id}" + provenance.target << questionnaireResponseReference + bundle.entry << create_bundle_entry(questionnaireResponse) + if serviceRequest + serviceRequestReference = FHIR::Reference.new + serviceRequestReference.reference = "urn:uuid:#{serviceRequest.id}" + provenance.target << serviceRequestReference + bundle.entry << create_bundle_entry(serviceRequest) + end + + entry.resource.meta = FHIR::Meta.new + entry.resource.meta.profile = [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-sdoh-assessment' ] + entry.resource.category = [ create_codeable_concept('http://terminology.hl7.org/CodeSystem/observation-category','survey','Survey') ] + entry.resource.category << create_codeable_concept('http://hl7.org/fhir/us/core/CodeSystem/us-core-tags', 'sdoh', 'SDOH') + entry.resource.hasMember = [] + entry.resource.derivedFrom = [ questionnaireResponseReference ] + entry.resource.component.each do |component| + instance = FHIR::Observation.new + instance.id = SecureRandom.uuid + instance.component = nil + instance.meta = FHIR::Meta.new + instance.meta.profile = [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-sdoh-assessment' ] + instance.category = [ create_codeable_concept('http://terminology.hl7.org/CodeSystem/observation-category','survey','Survey') ] + instance.category << create_codeable_concept('http://hl7.org/fhir/us/core/CodeSystem/us-core-tags', 'sdoh', 'SDOH') + instance.code = component.code + instance.derivedFrom = [ FHIR::Reference.new ] + instance.derivedFrom.first.reference = entry.fullUrl + if component.valueQuantity + instance.valueQuantity = component.valueQuantity + elsif component.valueString + instance.valueString = component.valueString + elsif component.valueCodeableConcept + instance.valueCodeableConcept = component.valueCodeableConcept + end + instance.status = entry.resource.status + instance.subject = entry.resource.subject + instance.encounter = entry.resource.encounter + instance.effectiveDateTime = entry.resource.effectiveDateTime + instance.issued = entry.resource.issued + reference = FHIR::Reference.new + reference.reference = "urn:uuid:#{instance.id}" + provenance.target << reference + entry.resource.hasMember << reference + bundle.entry << create_bundle_entry(instance) + end + prapare_count += 1 + + # add a sexual orientation observation to the same encounter the PRAPARE questionnaire was administered... + sexual_orientation_obs = FHIR::Observation.new({ + id: SecureRandom.uuid, + meta: { profile: [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-sexual-orientation' ] }, + category: [{ coding: [{ + system: 'http://terminology.hl7.org/CodeSystem/observation-category', + code: 'social-history', + display: 'Social History' + }]}], + code: { coding: [{ + system: 'http://loinc.org', + code: '76690-7', + display: 'Sexual orientation' + }]}, + subject: entry.resource.subject, + encounter: entry.resource.encounter, + status: 'final', + effectiveDateTime: entry.resource.effectiveDateTime, + valueCodeableConcept: sexual_orientation + }) + sexual_orientation_reference = FHIR::Reference.new + sexual_orientation_reference.reference = "urn:uuid:#{sexual_orientation_obs.id}" + provenance.target << sexual_orientation_reference + bundle.entry << create_bundle_entry(sexual_orientation_obs) + end + end + end + if prapare_count > 0 + puts " - Rewrote #{prapare_count} PRAPARE Observations..." + else + error(' * Rewrote 0 PRAPARE Observations.') + end + + puts ' - Rewriting Observation Profiles for v5...' + obs_profiles = Hash.new(0) + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + bundle.entry.each do |entry| + next unless entry.resource.resourceType == 'Observation' + next if entry.resource.meta&.profile&.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-sdoh-assessment') + + category = entry.resource.category&.first&.coding&.first&.code + code = entry.resource.code&.coding&.first&.code + if SURVEY_OBSERVATIONS.include?(code) + profile = 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-survey' + entry.resource.meta = FHIR::Meta.new + entry.resource.meta.profile = [ profile ] + entry.resource.category = [ create_codeable_concept('http://terminology.hl7.org/CodeSystem/observation-category','survey','Survey') ] + obs_profiles[profile] += 1 + elsif SDOH_ASSESSMENT_OBSERVATIONS.include?(code) + profile = 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-sdoh-assessment' + entry.resource.meta = FHIR::Meta.new + entry.resource.meta.profile = [ profile ] + entry.resource.category = [ create_codeable_concept('http://terminology.hl7.org/CodeSystem/observation-category','survey','Survey') ] + entry.resource.category << create_codeable_concept('http://hl7.org/fhir/us/core/CodeSystem/us-core-tags', 'sdoh', 'SDOH') + obs_profiles[profile] += 1 + elsif category == 'survey' + # catch any remaining survey obs that weren't SDOH related... + profile = 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-survey' + entry.resource.meta = FHIR::Meta.new + entry.resource.meta.profile = [ profile ] + entry.resource.category = [ create_codeable_concept('http://terminology.hl7.org/CodeSystem/observation-category','survey','Survey') ] + obs_profiles[profile] += 1 + end + end + end + obs_profiles.each do |profile,count| + puts " - Labeled #{count} instances of #{profile}" + end + + puts ' - Processing Observation Data Absent Reasons' + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + provenance = bundle.entry.find { |e| e.resource.is_a? FHIR::Provenance }.resource + break if observation_profiles.empty? + observation_profiles.delete_if do |profile_url| + entry = bundle.entry.find {|e| e.resource.resourceType == 'Observation' && e.resource.meta&.profile&.include?(profile_url) } + if entry + instance = FHIR::Json.from_json(entry.resource.to_json) + instance.id = SecureRandom.uuid + instance.dataAbsentReason = create_codeable_concept('http://terminology.hl7.org/CodeSystem/data-absent-reason', 'unknown', 'Unknown') + if observation_profiles_valueCodeableConcept_required.include?(profile_url) + instance.valueCodeableConcept = instance.dataAbsentReason + instance.dataAbsentReason = nil + elsif observation_profiles_components_required.include?(profile_url) + instance.component.each do |component| + component.valueQuantity = nil + component.dataAbsentReason = instance.dataAbsentReason + end + else + instance.valueQuantity = nil + instance.valueCodeableConcept = nil + instance.valueString = nil + end + new_entry = create_bundle_entry(instance) + provenance.target << FHIR::Reference.new + provenance.target.last.reference = "urn:uuid:#{instance.id}" + bundle.entry << new_entry + puts " - #{profile_url}: #{new_entry.fullUrl}" + true # delete this profile url from the list + else + false # keep searching for this profile url in the next bundle + end + end + end + unless observation_profiles.empty? + error(' * Missed Observation Data Absent Reasons') + observation_profiles.each do |profile_url| + error(" ** #{profile_url}") + end + end + + puts ' - Checking for Observation valueQuantity, valueCodeableConcept, and valueString...' + results.each do |bundle| + entries = bundle.entry.select {|entry| entry.resource.is_a?(FHIR::Observation)} + observations = entries.map {|entry| entry.resource} + # check for valueQuantity + hasValue = observations.any? {|observation| observation.valueQuantity != nil } + observation_values_found['valueQuantity'] = true if hasValue + # check for valueCodeableConcept + hasValue = observations.any? {|observation| observation.valueCodeableConcept != nil } + observation_values_found['valueCodeableConcept'] = true if hasValue + # check for valueString + hasValue = observations.any? {|observation| observation.valueString != nil } + observation_values_found['valueString'] = true if hasValue + break if observation_values_found.values.all? { |value| value == true } + end + observation_values_found.each do |key, value| + if value + puts " Found #{key}: #{value}" + else + error(" Found #{key}: #{value}") + end + end + + # remove all resources from bundles that are not US Core profiles + results.each do |bundle| + bundle.entry.delete_if {|e| ['Claim','ExplanationOfBenefit','ImagingStudy','MedicationAdministration','SupplyDelivery'].include?(e.resource.resourceType)} + end + puts ' - Removed resources out of scope for US Core.' + # There are probably some observations remaining after this that are not US Core profiles, + # but they likely are referenced from DiagnosticReports which are US Core profiled. + + # delete provenance references to removed resources + results.each do |bundle| + next if bundle.entry.first.resource.resourceType != 'Patient' # skip bundles of Organizations and Practitioners... + provenance = bundle.entry.find {|e| e.resource.resourceType == 'Provenance' }.resource + uuids = bundle.entry.map {|e| e.fullUrl} + provenance.target.keep_if {|reference| uuids.include?(reference.reference) } + end + puts ' - Rewrote Provenance targets.' + + DataScript::ChoiceTypeCreator.check_choice_types(results) + + # DiagnosticReports need to have two performer types, so we add them here + puts ' - Modifying DiagnosticReport performer types...' + dr_bundle = results.find do |b| + b.entry.any? do |e| + e.resource.is_a?(FHIR::DiagnosticReport) && e.resource.meta&.profile&.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-note') + end && + b.entry.any? do |e| + e.resource.is_a?(FHIR::DiagnosticReport) && e.resource.meta&.profile&.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab') + end + end + dr_notes = dr_bundle.entry.find_all do |e| + e.resource.is_a?(FHIR::DiagnosticReport) && e.resource.meta&.profile&.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-note') + end.map {|e| e.resource } + dr_labs = dr_bundle.entry.find_all do |e| + e.resource.is_a?(FHIR::DiagnosticReport) && e.resource.meta&.profile&.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-diagnosticreport-lab') + end.map {|e| e.resource } + practitioner_bundle = results.find {|b| b.entry.first.resource.resourceType == 'Practitioner'} + dr_practitioner = practitioner_bundle.entry.find { |e| e.resource.is_a? FHIR::Practitioner }.resource.identifier.first + organization_bundle = results.find {|b| b.entry.first.resource.resourceType == 'Organization'} + dr_organization = organization_bundle.entry.find { |e| e.resource.is_a? FHIR::Organization }.resource.identifier.first + puts " + DiagnosticReport Practitioner ID: Practitioner?identifier=#{dr_practitioner.system}|#{dr_practitioner.value}" + puts " + DiagnosticReport Organization ID: Organization?identifier=#{dr_organization.system}|#{dr_organization.value}" + dr_notes.concat(dr_labs).each do |dr| + # "performer": [ { + # "reference": "Practitioner?identifier=http://hl7.org/fhir/sid/us-npi|9999105593", + # "display": "Dr. Fernanda589 Huel628" + # } ], + dr.performer << { reference: "Practitioner?identifier=#{dr_practitioner.system}|#{dr_practitioner.value}" } + dr.performer << { reference: "Organization?identifier=#{dr_organization.system}|#{dr_organization.value}" } + end + + # The JSON from this exported patient will need to be manually altered to + # create primitive extensions, so we specifically return just this patient bundle. + selection_name + end + + def self.pick_by_gender(results) + # pick someone of the more represented gender + # in other words, if there are more males, pick a male. + # otherwise if there are more females, pick a female. + females = results.count {|b| b.entry.first.resource.resourceType == 'Patient' && b.entry.first.resource.gender == 'female'} + males = results.count {|b| b.entry.first.resource.resourceType == 'Patient' && b.entry.first.resource.gender == 'male'} + if males > females + selection = results.find {|b| b.entry.first.resource.resourceType == 'Patient' && b.entry.first.resource.gender == 'male'} + else + selection = results.find {|b| b.entry.first.resource.resourceType == 'Patient' && b.entry.first.resource.gender == 'female'} + end + selection + end + + def self.get_resource_by_id(bundle, id) + entry = bundle.entry.find { |entry| entry.resource.id == id || entry.fullUrl == id } + return entry.resource if entry + nil + end + + def self.remove_name(bundle) + # Replace name with empty name. + # Technically this doesn't validate because of us-core-8: + # Patient.name.given or Patient.name.family or both SHALL be present [family.exists() or given.exists()] + # + # The JSON from this exported patient will need to be manually altered to + # create primitive extensions. + human_name = FHIR::HumanName.new + # If the us-core-8 invariant changes to allow data absent reason on name, then we can enable the next line + # human_name.extension = [ data_absent_reason ] + bundle.entry.first.resource.name = [ human_name ] + end + + def self.alter_condition(bundle, rng) + # randomly pick one of their Conditions + random_condition = bundle.entry.map {|e| e.resource }.select {|r| r.resourceType == 'Condition'}.sample(random: rng) + # and replace the category with a data-absent-reason + unknown = FHIR::CodeableConcept.new + unknown.extension = [ data_absent_reason ] + random_condition.category = [ unknown ] + random_condition + end + + def self.data_absent_reason + extension = FHIR::Extension.new + extension.url = 'http://hl7.org/fhir/StructureDefinition/data-absent-reason' + extension.valueCode = 'unknown' + extension + end + + def self.asserted_date(date) + extension = FHIR::Extension.new + extension.url = 'http://hl7.org/fhir/StructureDefinition/condition-assertedDate' + extension.valueDateTime = date.split('T').first + extension + end + + def self.alter_smoking_status(bundle) + last_smoking_observation = bundle.entry.select {|e| e.resource.resourceType == 'Observation' && e.resource&.code&.text.start_with?('Tobacco smoking status') }.last.resource + coding = FHIR::Coding.new + coding.system = 'http://snomed.info/sct' + coding.code = '449868002' + coding.display = 'Current every day smoker' + smoker = FHIR::CodeableConcept.new + smoker.coding = [ coding ] + smoker.text = 'Current every day smoker' + last_smoking_observation.valueCodeableConcept = smoker + end + + def self.create_codeable_concept(system, code, display) + coding = FHIR::Coding.new + coding.system = system + coding.display = display + coding.code = code + codeableconcept = FHIR::CodeableConcept.new + codeableconcept.text = display + codeableconcept.coding = [ coding ] + codeableconcept + end + + def self.create_bundle_entry(resource) + entry = FHIR::Bundle::Entry.new + entry.fullUrl = "urn:uuid:#{resource.id}" + entry.resource = resource + entry.request = FHIR::Bundle::Entry::Request.new + entry.request.local_method = 'POST' + entry.request.url = resource.resourceType + entry + end + + PRAPARE_STRUCTURE = { + '93025-5' => { + '93043-8' => ['56051-6','32624-9','93035-4','93034-7','54899-0'], + '93042-0' => ['63512-8','71802-3','93033-9','56799-0'], + '93041-2' => ['82589-3','67875-5','76437-3','63586-2','93031-3','93030-5'], + '93040-4' => ['93029-7','93038-8'], + '93039-6' => ['93028-9','93027-1','93026-3','76501-6'] + } + } + + def self.create_questionnaire_response_from_multiobservation(observation) + serviceRequests = nil + questionnaireResponse = FHIR::QuestionnaireResponse.new + questionnaireResponse.id = SecureRandom.uuid + questionnaireResponse.meta = FHIR::Meta.new + questionnaireResponse.meta.profile = [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-questionnaireresponse' ] + questionnaireResponse.meta.tag = [ FHIR::Coding.new ] + questionnaireResponse.meta.tag.last.code = 'sdoh' + questionnaireResponse.meta.tag.last.display = 'SDOH' + questionnaireResponse.meta.tag.last.system = 'http://hl7.org/fhir/us/core/CodeSystem/us-core-tags' + questionnaireResponse.questionnaire = 'http://hl7.org/fhir/us/sdoh-clinicalcare/Questionnaire/SDOHCC-QuestionnairePRAPARE' + # TODO primitive extensiion 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-extension-questionnaire-uri' + questionnaireResponse.status = 'completed' + questionnaireResponse.subject = observation.subject + questionnaireResponse.encounter = observation.encounter + questionnaireResponse.authored = observation.issued + results = process_prapare_structure(PRAPARE_STRUCTURE, observation) + questionnaireResponse.item = results.first + serviceRequests = results.last&.first + [questionnaireResponse, serviceRequests] + end + + def self.process_prapare_structure(structure, observation, prefix=nil) + items = [] + serviceRequests = [] + if structure.is_a?(Hash) + structure.each do |key,value| + item = FHIR::QuestionnaireResponse::Item.new + item.linkId = "#{prefix}/#{key}" + component = observation.component.find {|c| c.code.coding.first.code == key} + item.text = component.code.text if component + prefix = value.is_a?(Array) ? "/#{key}" : nil + results = process_prapare_structure(value, observation, prefix) + item.item = results.first + serviceRequests.append(results.last).flatten! + items << item + end + elsif structure.is_a?(Array) + structure.each do |key| + item = FHIR::QuestionnaireResponse::Item.new + item.linkId = "#{prefix}/#{key}" + component = observation.component.find {|c| c.code.coding.first.code == key} + item.text = component.code.text if component + result = convert_obs_value_questionnaire_answer(component, observation) + item.answer = [ result.first ] + items << item + serviceRequest = result.last + serviceRequests << result.last unless result.last.nil? + end + end + return items, serviceRequests + end + + def self.convert_obs_value_questionnaire_answer(component, observation) + answer = FHIR::QuestionnaireResponse::Item::Answer.new + serviceRequest = nil + if component.valueQuantity + answer.valueQuantity = component.valueQuantity + elsif component.valueCodeableConcept + answer.valueCoding = component.valueCodeableConcept.coding.first + # create a service request is this for food.... + if component.valueCodeableConcept.coding.first.code == 'LA30125-1' # Food + serviceRequest = FHIR::ServiceRequest.new({ + id: SecureRandom.uuid, + meta: { profile: [ 'http://hl7.org/fhir/us/core/StructureDefinition/us-core-servicerequest' ] }, + category: [{ coding: [{ + system: 'http://hl7.org/fhir/us/core/CodeSystem/us-core-tags', + code: 'sdoh', + display: 'SDOH' + }], text: 'Social Determinants Of Health'}], + code: { coding: [{ + system: 'http://snomed.info/sct', + code: '467771000124109', + display: 'Assistance with application for food pantry program' + }]}, + subject: observation.subject, + encounter: observation.encounter, + status: 'completed', + intent: 'order', + occurrenceDateTime: observation.issued, + authoredOn: observation.issued + }) + end + elsif component.valueString + answer.valueString = component.valueString + end + return answer, serviceRequest + end + + def self.questionnaire_response_primitive_extension(json_string) + find_string = ' "questionnaire": "http://hl7.org/fhir/us/sdoh-clinicalcare/Questionnaire/SDOHCC-QuestionnairePRAPARE",' + replace_string = ' "questionnaire": "http://hl7.org/fhir/us/sdoh-clinicalcare/Questionnaire/SDOHCC-QuestionnairePRAPARE", + "_questionnaire" : { + "extension" : [ + { + "url" : "http://hl7.org/fhir/us/core/StructureDefinition/us-core-extension-questionnaire-uri", + "valueUri" : "https://prapare.org/wp-content/uploads/2021/10/PRAPARE-English.pdf" + } + ] + },' + json_string.gsub!(find_string, replace_string) + json_string + end + end +end diff --git a/lib/v5/validation_message_checks.rb b/lib/v5/validation_message_checks.rb new file mode 100644 index 0000000..f854a74 --- /dev/null +++ b/lib/v5/validation_message_checks.rb @@ -0,0 +1,44 @@ +module DataScript + class ValidationMessageChecks + def self.check(line) + return line if line.nil? + + # the tooling can't expand this valueset for some reason... + if (line.include?('The Coding provided (urn:oid:2.16.840.1.113883.6.238#1002-5) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category') || + line.include?('The Coding provided (urn:oid:2.16.840.1.113883.6.238#2028-9) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category') || + line.include?('The Coding provided (urn:oid:2.16.840.1.113883.6.238#2054-5) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category') || + line.include?('The Coding provided (urn:oid:2.16.840.1.113883.6.238#2076-8) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category') || + line.include?('The Coding provided (urn:oid:2.16.840.1.113883.6.238#2106-3) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category') || + line.include?('The Coding provided (urn:oid:2.16.840.1.113883.6.238#2131-1) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category') || + line.include?('The Coding provided (http://terminology.hl7.org/CodeSystem/v3-NullFlavor#ASKU) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category') || + line.include?('The Coding provided (http://terminology.hl7.org/CodeSystem/v3-NullFlavor#UNK) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-race-category') || + # the tooling can't expand this valueset for some reason... + line.include?('The Coding provided (urn:oid:2.16.840.1.113883.6.238#2135-2) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category') || + line.include?('The Coding provided (urn:oid:2.16.840.1.113883.6.238#2186-5) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category') || + line.include?('The Coding provided (http://terminology.hl7.org/CodeSystem/v3-NullFlavor#ASKU) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category') || + line.include?('The Coding provided (http://terminology.hl7.org/CodeSystem/v3-NullFlavor#UNK) is not in the value set http://hl7.org/fhir/us/core/ValueSet/omb-ethnicity-category') || + # the tooling can't expand this valueset for some reason... + line.include?("The value provided (\'F\') is not in the value set \'Birth Sex\' (http://hl7.org/fhir/us/core/ValueSet/birthsex") || + line.include?("The value provided (\'M\') is not in the value set \'Birth Sex\' (http://hl7.org/fhir/us/core/ValueSet/birthsex") || + line.include?("The value provided (\'ASKU\') is not in the value set \'Birth Sex\' (http://hl7.org/fhir/us/core/ValueSet/birthsex") || + line.include?("The value provided (\'OTH\') is not in the value set \'Birth Sex\' (http://hl7.org/fhir/us/core/ValueSet/birthsex") || + line.include?("The value provided (\'UNK\') is not in the value set \'Birth Sex\' (http://hl7.org/fhir/us/core/ValueSet/birthsex") || + # the tooling can't expand this valueset for some reason... + line.include?('The filter "concept = 768734005" from the value set http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1099.27 was not understood in the context of http://snomed.info/sct') || + # the tooling can't tell that references to our patient are to a us-core-patient + (line.include?('Unable to find a match for profile') && line.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient')) || + (line.include?('Unable to find a match for profile') && line.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-encounter')) || + (line.include?('Unable to find a match for profile') && line.include?('http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-lab')) || + # the invariant is wrong or the tooling can't figure it out + (line.include?('resource.ofType(DiagnosticReport)') && line.include?('us-core-8')) || + # the invariant is wrong or the tooling can't figure it out + (line.include?('resource.ofType(Provenance)') && line.include?('provenance-1')) || + # bizarre Rxnorm expansion error? + line.include?('Error from server: Unable to find value set http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.88.12.80.17')) + return nil #" IGNORE#{line.sub('Error','')}" + end + + line + end + end +end diff --git a/uscore-data-script.rb b/uscore-data-script.rb index 52ac52a..a9abb63 100644 --- a/uscore-data-script.rb +++ b/uscore-data-script.rb @@ -2,14 +2,52 @@ require 'fhir_models' require 'fileutils' require './lib/time.rb' -require './lib/constraints.rb' -require './lib/modifications.rb' require './lib/bulk_data_converter.rb' +require './lib/filter.rb' RAND_SEED = 3 +def error(message) + puts "\e[31m#{message}\e[0m" +end + start = Time.now.to_i +if ARGV && ARGV.length >= 1 + if ARGV.include?('v3') + puts 'Using US Core version 3...' + VERSION = 3 + require './lib/v3/constraints.rb' + require './lib/v3/modifications.rb' + require './lib/v3/choice_types.rb' + elsif ARGV.include?('v4') + puts 'Using US Core version 4...' + VERSION = 4 + require './lib/v4/constraints.rb' + require './lib/v4/modifications.rb' + require './lib/v4/choice_types.rb' + elsif ARGV.include?('v5') + puts 'Using US Core version 5...' + VERSION = 5 + require './lib/v5/constraints.rb' + require './lib/v5/modifications.rb' + require './lib/v5/choice_types.rb' + require './lib/v5/validation_message_checks.rb' + else + puts 'Using US Core version 4...' + VERSION = 4 + require './lib/v4/constraints.rb' + require './lib/v4/modifications.rb' + require './lib/v4/choice_types.rb' + end +else + puts 'Using US Core version 4...' + VERSION = 4 + require './lib/v4/constraints.rb' + require './lib/v4/modifications.rb' + require './lib/v4/choice_types.rb' +end + if ARGV && ARGV.length >= 1 && ARGV.include?('mrburns') puts 'Generating Mr. Burns...' MRBURNS=true @@ -22,6 +60,33 @@ MRBURNS = false end +def validate(filename, validation_file) + uscore_ver = '3.1.1' + uscore_ver = '4.0.0' if VERSION == 4 + uscore_ver = '5.0.1' if VERSION == 5 + system( "java -jar lib/validator_cli.jar #{filename} -sct us -version 4.0.1 -ig hl7.fhir.us.core##{uscore_ver} > #{validation_file} 2>&1" ) + + if VERSION == 5 + logfile = File.open(validation_file, 'r:UTF-8') + filepath = validation_file.split(File::Separator) + filepath[filepath.length-1] = "_#{filepath.last}" + modifiedFilename = filepath.join(File::Separator) + modifiedLogFile = File.open(modifiedFilename, 'w:UTF-8') + + logfile.each do |line| + if line.start_with?(' Error @ Bundle.entry') + new_line = DataScript::ValidationMessageChecks.check(line) + modifiedLogFile.write(new_line) unless new_line.nil? + else + modifiedLogFile.write(line) + end + end + + logfile.close + modifiedLogFile.close + end +end + puts 'Generating Synthetic Patients with Synthea...' output = 'output' output_raw = 'output/raw' @@ -33,14 +98,19 @@ FileUtils.rm Dir.glob("./#{output_raw_fhir}/*.json") # Manually list out the classpath, because it needs to be loaded in a specific order... -CLASSPATH='lib/synthea/synthea.jar:lib/synthea/SimulationCoreLibrary_v1.5_slim.jar:lib/synthea/hapi-fhir-structures-dstu3-4.1.0.jar:lib/synthea/hapi-fhir-structures-dstu2-4.1.0.jar:lib/synthea/hapi-fhir-structures-r4-4.1.0.jar:lib/synthea/org.hl7.fhir.dstu3-4.1.0.jar:lib/synthea/org.hl7.fhir.r4-4.1.0.jar:lib/synthea/org.hl7.fhir.utilities-4.1.0.jar:lib/synthea/hapi-fhir-base-4.1.0.jar:lib/synthea/gson-2.8.5.jar:lib/synthea/json-path-2.4.0.jar:lib/synthea/freemarker-2.3.26-incubating.jar:lib/synthea/h2-1.4.196.jar:lib/synthea/guava-28.0-jre.jar:lib/synthea/graphviz-java-0.2.2.jar:lib/synthea/commons-csv-1.5.jar:lib/synthea/jackson-dataformat-csv-2.8.8.jar:lib/synthea/snakeyaml-1.25.jar:lib/synthea/commons-math3-3.6.1.jar:lib/synthea/commons-text-1.7.jar:lib/synthea/cql-engine-1.3.10-SNAPSHOT.jar:lib/synthea/cql-to-elm-1.3.17.jar:lib/synthea/cql-1.3.17.jar:lib/synthea/elm-1.3.17.jar:lib/synthea/model-1.3.17.jar:lib/synthea/jaxb-runtime-2.3.0.jar:lib/synthea/jaxb-core-2.3.0.jar:lib/synthea/jaxb-api-2.3.0.jar:lib/synthea/activation-1.1.1.jar:lib/synthea/quick-1.3.17.jar:lib/synthea/qdm-1.3.17.jar:lib/synthea/jaxb2-basics-0.9.4.jar:lib/synthea/jaxb2-basics-tools-0.9.4.jar:lib/synthea/jcl-over-slf4j-1.7.28.jar:lib/synthea/jul-to-slf4j-1.7.25.jar:lib/synthea/slf4j-log4j12-1.7.25.jar:lib/synthea/jsbml-1.4.jar:lib/synthea/jsbml-arrays-1.4.jar:lib/synthea/jsbml-comp-1.4.jar:lib/synthea/jsbml-distrib-1.3.1.jar:lib/synthea/jsbml-dyn-1.4.jar:lib/synthea/jsbml-fbc-1.4.jar:lib/synthea/jsbml-groups-1.4.jar:lib/synthea/jsbml-render-1.4.jar:lib/synthea/jsbml-layout-1.4.jar:lib/synthea/jsbml-multi-1.4.jar:lib/synthea/jsbml-qual-1.4.jar:lib/synthea/jsbml-req-1.4.jar:lib/synthea/jsbml-spatial-1.4.jar:lib/synthea/jsbml-tidy-1.4.jar:lib/synthea/jsbml-core-1.4.jar:lib/synthea/biojava-ontology-4.0.0.jar:lib/synthea/log4j-slf4j-impl-2.1.jar:lib/synthea/slf4j-api-1.7.28.jar:lib/synthea/commons-math-2.2.jar:lib/synthea/jfreechart-1.5.0.jar:lib/synthea/json-smart-2.3.jar:lib/synthea/commons-lang3-3.9.jar:lib/synthea/commons-codec-1.12.jar:lib/synthea/batik-codec-1.9.jar:lib/synthea/batik-rasterizer-1.9.jar:lib/synthea/batik-svgrasterizer-1.9.jar:lib/synthea/batik-transcoder-1.9.jar:lib/synthea/batik-bridge-1.9.jar:lib/synthea/batik-script-1.9.jar:lib/synthea/batik-anim-1.9.jar:lib/synthea/batik-svg-dom-1.9.jar:lib/synthea/batik-dom-1.9.jar:lib/synthea/batik-css-1.9.jar:lib/synthea/xmlgraphics-commons-2.2.jar:lib/synthea/commons-io-2.6.jar:lib/synthea/ucum-1.0.2.jar:lib/synthea/jsr305-3.0.2.jar:lib/synthea/j2v8_macosx_x86_64-4.6.0.jar:lib/synthea/j2v8_linux_x86_64-4.6.0.jar:lib/synthea/j2v8_win32_x86_64-4.6.0.jar:lib/synthea/j2v8_win32_x86-4.6.0.jar:lib/synthea/commons-exec-1.3.jar:lib/synthea/jackson-databind-2.10.1.jar:lib/synthea/jackson-core-2.10.1.jar:lib/synthea/jackson-annotations-2.10.1.jar:lib/synthea/jaxb2-fluent-api-3.0.jar:lib/synthea/hamcrest-all-1.3.jar:lib/synthea/hamcrest-json-0.2.jar:lib/synthea/jaxb-impl-2.3.0.1.jar:lib/synthea/jaxb-core-2.3.0.1.jar:lib/synthea/javax.activation-1.2.0.jar:lib/synthea/eclipselink-2.6.0.jar:lib/synthea/validation-api-1.1.0.Final.jar:lib/synthea/antlr4-4.5.jar:lib/synthea/jopt-simple-4.7.jar:lib/synthea/stax-ex-1.7.8.jar:lib/synthea/FastInfoset-1.2.13.jar:lib/synthea/accessors-smart-1.2.jar:lib/synthea/failureaccess-1.0.1.jar:lib/synthea/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:lib/synthea/checker-qual-2.8.1.jar:lib/synthea/error_prone_annotations-2.3.2.jar:lib/synthea/j2objc-annotations-1.3.jar:lib/synthea/animal-sniffer-annotations-1.17.jar:lib/synthea/xpp3-1.1.4c.jar:lib/synthea/xpp3_xpath-1.1.4c.jar:lib/synthea/json-simple-1.1.1.jar:lib/synthea/junit-4.12.jar:lib/synthea/batik-parser-1.9.jar:lib/synthea/batik-gvt-1.9.jar:lib/synthea/batik-svggen-1.9.jar:lib/synthea/batik-awt-util-1.9.jar:lib/synthea/batik-xml-1.9.jar:lib/synthea/batik-util-1.9.jar:lib/synthea/xalan-2.7.2.jar:lib/synthea/serializer-2.7.2.jar:lib/synthea/xml-apis-1.3.04.jar:lib/synthea/jaxb2-basics-runtime-0.9.4.jar:lib/synthea/javaparser-1.0.11.jar:lib/synthea/jsonassert-1.1.1.jar:lib/synthea/hamcrest-core-1.3.jar:lib/synthea/log4j-1.2.17.jar:lib/synthea/javax.persistence-2.1.0.jar:lib/synthea/commonj.sdo-2.1.1.jar:lib/synthea/javax.json-1.0.4.jar:lib/synthea/antlr4-runtime-4.5.jar:lib/synthea/ST4-4.0.8.jar:lib/synthea/antlr-runtime-3.5.2.jar:lib/synthea/txw2-2.3.0.jar:lib/synthea/istack-commons-runtime-3.0.5.jar:lib/synthea/log4j-1.2-api-2.3.jar:lib/synthea/log4j-core-2.3.jar:lib/synthea/woodstox-core-5.0.1.jar:lib/synthea/jigsaw-2.2.6.jar:lib/synthea/xstream-1.3.1.jar:lib/synthea/staxmate-2.3.0.jar:lib/synthea/jtidy-r938.jar:lib/synthea/asm-5.0.4.jar:lib/synthea/batik-ext-1.9.jar:lib/synthea/xml-apis-ext-1.3.04.jar:lib/synthea/batik-constants-1.9.jar:lib/synthea/batik-i18n-1.9.jar:lib/synthea/commons-beanutils-1.9.2.jar:lib/synthea/json-20090211.jar:lib/synthea/commons-collections-3.2.1.jar:lib/synthea/org.abego.treelayout.core-1.0.1.jar:lib/synthea/log4j-api-2.3.jar:lib/synthea/stax2-api-3.1.4.jar:lib/synthea/xpp3_min-1.1.4c.jar:lib/synthea/commons-logging-1.0.4.jar' - -CONFIG='--exporter.fhir.use_us_core_ig=true --exporter.baseDirectory=./output/raw --exporter.hospital.fhir.export=false --exporter.practitioner.fhir.export=false --exporter.groups.fhir.export=true' +if VERSION == 3 + CLASSPATH='lib/synthea_uscore_v3/synthea.jar:lib/synthea_uscore_v3/SimulationCoreLibrary_v1.5_slim.jar:lib/synthea_uscore_v3/hapi-fhir-structures-dstu3-4.1.0.jar:lib/synthea_uscore_v3/hapi-fhir-structures-dstu2-4.1.0.jar:lib/synthea_uscore_v3/hapi-fhir-structures-r4-4.1.0.jar:lib/synthea_uscore_v3/org.hl7.fhir.dstu3-4.1.0.jar:lib/synthea_uscore_v3/org.hl7.fhir.r4-4.1.0.jar:lib/synthea_uscore_v3/org.hl7.fhir.utilities-4.1.0.jar:lib/synthea_uscore_v3/hapi-fhir-base-4.1.0.jar:lib/synthea_uscore_v3/gson-2.8.5.jar:lib/synthea_uscore_v3/json-path-2.4.0.jar:lib/synthea_uscore_v3/freemarker-2.3.26-incubating.jar:lib/synthea_uscore_v3/h2-1.4.196.jar:lib/synthea_uscore_v3/guava-28.0-jre.jar:lib/synthea_uscore_v3/graphviz-java-0.2.2.jar:lib/synthea_uscore_v3/commons-csv-1.5.jar:lib/synthea_uscore_v3/jackson-dataformat-csv-2.8.8.jar:lib/synthea_uscore_v3/snakeyaml-1.25.jar:lib/synthea_uscore_v3/commons-math3-3.6.1.jar:lib/synthea_uscore_v3/commons-text-1.7.jar:lib/synthea_uscore_v3/cql-engine-1.3.10-SNAPSHOT.jar:lib/synthea_uscore_v3/cql-to-elm-1.3.17.jar:lib/synthea_uscore_v3/cql-1.3.17.jar:lib/synthea_uscore_v3/elm-1.3.17.jar:lib/synthea_uscore_v3/model-1.3.17.jar:lib/synthea_uscore_v3/jaxb-runtime-2.3.0.jar:lib/synthea_uscore_v3/jaxb-core-2.3.0.jar:lib/synthea_uscore_v3/jaxb-api-2.3.0.jar:lib/synthea_uscore_v3/activation-1.1.1.jar:lib/synthea_uscore_v3/quick-1.3.17.jar:lib/synthea_uscore_v3/qdm-1.3.17.jar:lib/synthea_uscore_v3/jaxb2-basics-0.9.4.jar:lib/synthea_uscore_v3/jaxb2-basics-tools-0.9.4.jar:lib/synthea_uscore_v3/jcl-over-slf4j-1.7.28.jar:lib/synthea_uscore_v3/jul-to-slf4j-1.7.25.jar:lib/synthea_uscore_v3/slf4j-log4j12-1.7.25.jar:lib/synthea_uscore_v3/jsbml-1.4.jar:lib/synthea_uscore_v3/jsbml-arrays-1.4.jar:lib/synthea_uscore_v3/jsbml-comp-1.4.jar:lib/synthea_uscore_v3/jsbml-distrib-1.3.1.jar:lib/synthea_uscore_v3/jsbml-dyn-1.4.jar:lib/synthea_uscore_v3/jsbml-fbc-1.4.jar:lib/synthea_uscore_v3/jsbml-groups-1.4.jar:lib/synthea_uscore_v3/jsbml-render-1.4.jar:lib/synthea_uscore_v3/jsbml-layout-1.4.jar:lib/synthea_uscore_v3/jsbml-multi-1.4.jar:lib/synthea_uscore_v3/jsbml-qual-1.4.jar:lib/synthea_uscore_v3/jsbml-req-1.4.jar:lib/synthea_uscore_v3/jsbml-spatial-1.4.jar:lib/synthea_uscore_v3/jsbml-tidy-1.4.jar:lib/synthea_uscore_v3/jsbml-core-1.4.jar:lib/synthea_uscore_v3/biojava-ontology-4.0.0.jar:lib/synthea_uscore_v3/log4j-slf4j-impl-2.1.jar:lib/synthea_uscore_v3/slf4j-api-1.7.28.jar:lib/synthea_uscore_v3/commons-math-2.2.jar:lib/synthea_uscore_v3/jfreechart-1.5.0.jar:lib/synthea_uscore_v3/json-smart-2.3.jar:lib/synthea_uscore_v3/commons-lang3-3.9.jar:lib/synthea_uscore_v3/commons-codec-1.12.jar:lib/synthea_uscore_v3/batik-codec-1.9.jar:lib/synthea_uscore_v3/batik-rasterizer-1.9.jar:lib/synthea_uscore_v3/batik-svgrasterizer-1.9.jar:lib/synthea_uscore_v3/batik-transcoder-1.9.jar:lib/synthea_uscore_v3/batik-bridge-1.9.jar:lib/synthea_uscore_v3/batik-script-1.9.jar:lib/synthea_uscore_v3/batik-anim-1.9.jar:lib/synthea_uscore_v3/batik-svg-dom-1.9.jar:lib/synthea_uscore_v3/batik-dom-1.9.jar:lib/synthea_uscore_v3/batik-css-1.9.jar:lib/synthea_uscore_v3/xmlgraphics-commons-2.2.jar:lib/synthea_uscore_v3/commons-io-2.6.jar:lib/synthea_uscore_v3/ucum-1.0.2.jar:lib/synthea_uscore_v3/jsr305-3.0.2.jar:lib/synthea_uscore_v3/j2v8_macosx_x86_64-4.6.0.jar:lib/synthea_uscore_v3/j2v8_linux_x86_64-4.6.0.jar:lib/synthea_uscore_v3/j2v8_win32_x86_64-4.6.0.jar:lib/synthea_uscore_v3/j2v8_win32_x86-4.6.0.jar:lib/synthea_uscore_v3/commons-exec-1.3.jar:lib/synthea_uscore_v3/jackson-databind-2.10.1.jar:lib/synthea_uscore_v3/jackson-core-2.10.1.jar:lib/synthea_uscore_v3/jackson-annotations-2.10.1.jar:lib/synthea_uscore_v3/jaxb2-fluent-api-3.0.jar:lib/synthea_uscore_v3/hamcrest-all-1.3.jar:lib/synthea_uscore_v3/hamcrest-json-0.2.jar:lib/synthea_uscore_v3/jaxb-impl-2.3.0.1.jar:lib/synthea_uscore_v3/jaxb-core-2.3.0.1.jar:lib/synthea_uscore_v3/javax.activation-1.2.0.jar:lib/synthea_uscore_v3/eclipselink-2.6.0.jar:lib/synthea_uscore_v3/validation-api-1.1.0.Final.jar:lib/synthea_uscore_v3/antlr4-4.5.jar:lib/synthea_uscore_v3/jopt-simple-4.7.jar:lib/synthea_uscore_v3/stax-ex-1.7.8.jar:lib/synthea_uscore_v3/FastInfoset-1.2.13.jar:lib/synthea_uscore_v3/accessors-smart-1.2.jar:lib/synthea_uscore_v3/failureaccess-1.0.1.jar:lib/synthea_uscore_v3/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:lib/synthea_uscore_v3/checker-qual-2.8.1.jar:lib/synthea_uscore_v3/error_prone_annotations-2.3.2.jar:lib/synthea_uscore_v3/j2objc-annotations-1.3.jar:lib/synthea_uscore_v3/animal-sniffer-annotations-1.17.jar:lib/synthea_uscore_v3/xpp3-1.1.4c.jar:lib/synthea_uscore_v3/xpp3_xpath-1.1.4c.jar:lib/synthea_uscore_v3/json-simple-1.1.1.jar:lib/synthea_uscore_v3/junit-4.12.jar:lib/synthea_uscore_v3/batik-parser-1.9.jar:lib/synthea_uscore_v3/batik-gvt-1.9.jar:lib/synthea_uscore_v3/batik-svggen-1.9.jar:lib/synthea_uscore_v3/batik-awt-util-1.9.jar:lib/synthea_uscore_v3/batik-xml-1.9.jar:lib/synthea_uscore_v3/batik-util-1.9.jar:lib/synthea_uscore_v3/xalan-2.7.2.jar:lib/synthea_uscore_v3/serializer-2.7.2.jar:lib/synthea_uscore_v3/xml-apis-1.3.04.jar:lib/synthea_uscore_v3/jaxb2-basics-runtime-0.9.4.jar:lib/synthea_uscore_v3/javaparser-1.0.11.jar:lib/synthea_uscore_v3/jsonassert-1.1.1.jar:lib/synthea_uscore_v3/hamcrest-core-1.3.jar:lib/synthea_uscore_v3/log4j-1.2.17.jar:lib/synthea_uscore_v3/javax.persistence-2.1.0.jar:lib/synthea_uscore_v3/commonj.sdo-2.1.1.jar:lib/synthea_uscore_v3/javax.json-1.0.4.jar:lib/synthea_uscore_v3/antlr4-runtime-4.5.jar:lib/synthea_uscore_v3/ST4-4.0.8.jar:lib/synthea_uscore_v3/antlr-runtime-3.5.2.jar:lib/synthea_uscore_v3/txw2-2.3.0.jar:lib/synthea_uscore_v3/istack-commons-runtime-3.0.5.jar:lib/synthea_uscore_v3/log4j-1.2-api-2.3.jar:lib/synthea_uscore_v3/log4j-core-2.3.jar:lib/synthea_uscore_v3/woodstox-core-5.0.1.jar:lib/synthea_uscore_v3/jigsaw-2.2.6.jar:lib/synthea_uscore_v3/xstream-1.3.1.jar:lib/synthea_uscore_v3/staxmate-2.3.0.jar:lib/synthea_uscore_v3/jtidy-r938.jar:lib/synthea_uscore_v3/asm-5.0.4.jar:lib/synthea_uscore_v3/batik-ext-1.9.jar:lib/synthea_uscore_v3/xml-apis-ext-1.3.04.jar:lib/synthea_uscore_v3/batik-constants-1.9.jar:lib/synthea_uscore_v3/batik-i18n-1.9.jar:lib/synthea_uscore_v3/commons-beanutils-1.9.2.jar:lib/synthea_uscore_v3/json-20090211.jar:lib/synthea_uscore_v3/commons-collections-3.2.1.jar:lib/synthea_uscore_v3/org.abego.treelayout.core-1.0.1.jar:lib/synthea_uscore_v3/log4j-api-2.3.jar:lib/synthea_uscore_v3/stax2-api-3.1.4.jar:lib/synthea_uscore_v3/xpp3_min-1.1.4c.jar:lib/synthea_uscore_v3/commons-logging-1.0.4.jar' + CONFIG='--exporter.fhir.use_us_core_ig=true --exporter.baseDirectory=./output/raw --exporter.hospital.fhir.export=false --exporter.practitioner.fhir.export=false --exporter.groups.fhir.export=true' +elsif VERSION >= 4 + CLASSPATH='lib/synthea_uscore_v4/synthea.jar:lib/synthea_uscore_v4/SimulationCoreLibrary_v1.5_slim.jar:lib/synthea_uscore_v4/gson-2.8.7.jar:lib/synthea_uscore_v4/json-path-2.4.0.jar:lib/synthea_uscore_v4/hapi-fhir-structures-dstu3-5.7.0.jar:lib/synthea_uscore_v4/hapi-fhir-structures-dstu2-5.7.0.jar:lib/synthea_uscore_v4/hapi-fhir-structures-r4-5.7.0.jar:lib/synthea_uscore_v4/hapi-fhir-client-5.7.0.jar:lib/synthea_uscore_v4/org.hl7.fhir.dstu3-5.6.27.jar:lib/synthea_uscore_v4/org.hl7.fhir.r4-5.6.27.jar:lib/synthea_uscore_v4/org.hl7.fhir.utilities-5.6.27.jar:lib/synthea_uscore_v4/hapi-fhir-base-5.7.0.jar:lib/synthea_uscore_v4/freemarker-2.3.26-incubating.jar:lib/synthea_uscore_v4/guava-31.0.1-jre.jar:lib/synthea_uscore_v4/graphviz-java-0.2.4.jar:lib/synthea_uscore_v4/commons-csv-1.5.jar:lib/synthea_uscore_v4/jackson-datatype-jsr310-2.13.1.jar:lib/synthea_uscore_v4/cql-engine-1.3.12.jar:lib/synthea_uscore_v4/cql-to-elm-1.3.17.jar:lib/synthea_uscore_v4/jackson-databind-2.13.1.jar:lib/synthea_uscore_v4/jackson-annotations-2.13.1.jar:lib/synthea_uscore_v4/jackson-core-2.13.1.jar:lib/synthea_uscore_v4/jackson-dataformat-csv-2.13.1.jar:lib/synthea_uscore_v4/snakeyaml-1.27.jar:lib/synthea_uscore_v4/commons-math3-3.6.1.jar:lib/synthea_uscore_v4/commons-text-1.9.jar:lib/synthea_uscore_v4/commons-validator-1.4.0.jar:lib/synthea_uscore_v4/cql-1.3.17.jar:lib/synthea_uscore_v4/elm-1.3.17.jar:lib/synthea_uscore_v4/model-1.3.17.jar:lib/synthea_uscore_v4/spring-web-5.2.7.RELEASE.jar:lib/synthea_uscore_v4/jaxb-api-2.4.0-b180830.0359.jar:lib/synthea_uscore_v4/jaxb-runtime-2.3.2.jar:lib/synthea_uscore_v4/javax.activation-api-1.2.0.jar:lib/synthea_uscore_v4/quick-1.3.17.jar:lib/synthea_uscore_v4/qdm-1.3.17.jar:lib/synthea_uscore_v4/jaxb2-basics-0.12.0.jar:lib/synthea_uscore_v4/jaxb2-basics-tools-0.12.0.jar:lib/synthea_uscore_v4/jcl-over-slf4j-1.7.33.jar:lib/synthea_uscore_v4/jul-to-slf4j-1.7.25.jar:lib/synthea_uscore_v4/slf4j-log4j12-1.7.25.jar:lib/synthea_uscore_v4/jsbml-1.5.jar:lib/synthea_uscore_v4/jsbml-arrays-1.5.jar:lib/synthea_uscore_v4/jsbml-comp-1.5.jar:lib/synthea_uscore_v4/jsbml-distrib-1.5.jar:lib/synthea_uscore_v4/jsbml-dyn-1.5.jar:lib/synthea_uscore_v4/jsbml-fbc-1.5.jar:lib/synthea_uscore_v4/jsbml-groups-1.5.jar:lib/synthea_uscore_v4/jsbml-render-1.5.jar:lib/synthea_uscore_v4/jsbml-layout-1.5.jar:lib/synthea_uscore_v4/jsbml-multi-1.5.jar:lib/synthea_uscore_v4/jsbml-qual-1.5.jar:lib/synthea_uscore_v4/jsbml-req-1.5.jar:lib/synthea_uscore_v4/jsbml-spatial-1.5.jar:lib/synthea_uscore_v4/jsbml-tidy-1.5.jar:lib/synthea_uscore_v4/jsbml-core-1.5.jar:lib/synthea_uscore_v4/biojava-ontology-4.0.0.jar:lib/synthea_uscore_v4/slf4j-api-1.7.33.jar:lib/synthea_uscore_v4/log4j-1.2-api-2.3.jar:lib/synthea_uscore_v4/log4j-core-2.17.0.jar:lib/synthea_uscore_v4/commons-math-2.2.jar:lib/synthea_uscore_v4/jfreechart-1.5.0.jar:lib/synthea_uscore_v4/json-smart-2.3.jar:lib/synthea_uscore_v4/commons-lang3-3.12.0.jar:lib/synthea_uscore_v4/httpclient-4.5.13.jar:lib/synthea_uscore_v4/commons-codec-1.15.jar:lib/synthea_uscore_v4/batik-codec-1.9.jar:lib/synthea_uscore_v4/batik-rasterizer-1.9.jar:lib/synthea_uscore_v4/batik-svgrasterizer-1.9.jar:lib/synthea_uscore_v4/batik-transcoder-1.9.jar:lib/synthea_uscore_v4/batik-bridge-1.9.jar:lib/synthea_uscore_v4/batik-script-1.9.jar:lib/synthea_uscore_v4/batik-anim-1.9.jar:lib/synthea_uscore_v4/batik-svg-dom-1.9.jar:lib/synthea_uscore_v4/batik-dom-1.9.jar:lib/synthea_uscore_v4/batik-css-1.9.jar:lib/synthea_uscore_v4/xmlgraphics-commons-2.2.jar:lib/synthea_uscore_v4/commons-io-2.11.0.jar:lib/synthea_uscore_v4/jsr305-3.0.2.jar:lib/synthea_uscore_v4/okhttp-3.8.1.jar:lib/synthea_uscore_v4/httpcore-4.4.13.jar:lib/synthea_uscore_v4/j2v8_macosx_x86_64-4.6.0.jar:lib/synthea_uscore_v4/j2v8_linux_x86_64-4.6.0.jar:lib/synthea_uscore_v4/j2v8_win32_x86_64-4.6.0.jar:lib/synthea_uscore_v4/j2v8_win32_x86-4.6.0.jar:lib/synthea_uscore_v4/commons-exec-1.3.jar:lib/synthea_uscore_v4/commons-beanutils-1.9.3.jar:lib/synthea_uscore_v4/commons-digester-1.8.jar:lib/synthea_uscore_v4/commons-logging-1.2.jar:lib/synthea_uscore_v4/hamcrest-all-1.3.jar:lib/synthea_uscore_v4/hamcrest-json-0.2.jar:lib/synthea_uscore_v4/jaxb-impl-2.3.0.1.jar:lib/synthea_uscore_v4/jaxb-core-2.3.0.1.jar:lib/synthea_uscore_v4/javax.activation-1.2.0.jar:lib/synthea_uscore_v4/eclipselink-2.6.0.jar:lib/synthea_uscore_v4/validation-api-1.1.0.Final.jar:lib/synthea_uscore_v4/antlr4-4.5.jar:lib/synthea_uscore_v4/jopt-simple-4.7.jar:lib/synthea_uscore_v4/ucum-1.0.2.jar:lib/synthea_uscore_v4/spring-beans-5.2.7.RELEASE.jar:lib/synthea_uscore_v4/spring-core-5.2.7.RELEASE.jar:lib/synthea_uscore_v4/stax-ex-1.8.1.jar:lib/synthea_uscore_v4/jakarta.xml.bind-api-2.3.2.jar:lib/synthea_uscore_v4/txw2-2.3.2.jar:lib/synthea_uscore_v4/istack-commons-runtime-3.0.8.jar:lib/synthea_uscore_v4/FastInfoset-1.2.16.jar:lib/synthea_uscore_v4/jakarta.activation-api-1.2.1.jar:lib/synthea_uscore_v4/log4j-api-2.17.0.jar:lib/synthea_uscore_v4/accessors-smart-1.2.jar:lib/synthea_uscore_v4/failureaccess-1.0.1.jar:lib/synthea_uscore_v4/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:lib/synthea_uscore_v4/checker-qual-3.12.0.jar:lib/synthea_uscore_v4/error_prone_annotations-2.7.1.jar:lib/synthea_uscore_v4/j2objc-annotations-1.3.jar:lib/synthea_uscore_v4/okio-1.13.0.jar:lib/synthea_uscore_v4/batik-parser-1.9.jar:lib/synthea_uscore_v4/batik-gvt-1.9.jar:lib/synthea_uscore_v4/batik-svggen-1.9.jar:lib/synthea_uscore_v4/batik-awt-util-1.9.jar:lib/synthea_uscore_v4/batik-xml-1.9.jar:lib/synthea_uscore_v4/batik-util-1.9.jar:lib/synthea_uscore_v4/xalan-2.7.2.jar:lib/synthea_uscore_v4/serializer-2.7.2.jar:lib/synthea_uscore_v4/xml-apis-1.3.04.jar:lib/synthea_uscore_v4/jaxb2-basics-runtime-0.12.0.jar:lib/synthea_uscore_v4/javaparser-1.0.11.jar:lib/synthea_uscore_v4/jsonassert-1.1.1.jar:lib/synthea_uscore_v4/json-simple-1.1.1.jar:lib/synthea_uscore_v4/junit-4.12.jar:lib/synthea_uscore_v4/hamcrest-core-1.3.jar:lib/synthea_uscore_v4/log4j-1.2.17.jar:lib/synthea_uscore_v4/javax.persistence-2.1.0.jar:lib/synthea_uscore_v4/commonj.sdo-2.1.1.jar:lib/synthea_uscore_v4/javax.json-1.0.4.jar:lib/synthea_uscore_v4/antlr4-runtime-4.5.jar:lib/synthea_uscore_v4/ST4-4.0.8.jar:lib/synthea_uscore_v4/antlr-runtime-3.5.2.jar:lib/synthea_uscore_v4/xpp3-1.1.4c.jar:lib/synthea_uscore_v4/xpp3_xpath-1.1.4c.jar:lib/synthea_uscore_v4/spring-jcl-5.2.7.RELEASE.jar:lib/synthea_uscore_v4/woodstox-core-5.0.1.jar:lib/synthea_uscore_v4/jigsaw-2.2.6.jar:lib/synthea_uscore_v4/xstream-1.4.9.jar:lib/synthea_uscore_v4/staxmate-2.3.0.jar:lib/synthea_uscore_v4/jtidy-r938.jar:lib/synthea_uscore_v4/asm-5.0.4.jar:lib/synthea_uscore_v4/batik-ext-1.9.jar:lib/synthea_uscore_v4/xml-apis-ext-1.3.04.jar:lib/synthea_uscore_v4/batik-constants-1.9.jar:lib/synthea_uscore_v4/batik-i18n-1.9.jar:lib/synthea_uscore_v4/json-20090211.jar:lib/synthea_uscore_v4/commons-collections-3.2.2.jar:lib/synthea_uscore_v4/org.abego.treelayout.core-1.0.1.jar:lib/synthea_uscore_v4/stax2-api-3.1.4.jar:lib/synthea_uscore_v4/xmlpull-1.1.3.1.jar:lib/synthea_uscore_v4/xpp3_min-1.1.4c.jar' + CONFIG='--exporter.fhir.use_us_core_ig=true --exporter.baseDirectory=./output/raw --exporter.hospital.fhir.export=true --exporter.practitioner.fhir.export=true --exporter.groups.fhir.export=true' +end if MRBURNS system( "java -cp #{CLASSPATH} App -s #{RAND_SEED} -a 80-81 -g M -p 50 #{CONFIG} --exporter.years_of_history=0 > output/synthea.log" ) else system( "java -cp #{CLASSPATH} App -s #{RAND_SEED} -p 160 #{CONFIG} > output/synthea.log" ) + # system( "java -cp #{CLASSPATH} App -s #{RAND_SEED} -p 10 #{CONFIG} > output/synthea.log" ) end tok = Time.now.to_i puts " Generated data in #{DataScript::TimeUtilities.pretty(tok - start)}." @@ -51,7 +121,7 @@ input_folder = File.join(File.dirname(__FILE__), './output/raw/fhir') Dir.foreach(input_folder) do |file| next unless file.end_with?('.json') - next if file.start_with?('hospitalInformation', 'practitionerInformation') + next if VERSION == 3 && file.start_with?('hospitalInformation', 'practitionerInformation') json = File.open("#{input_folder}/#{file}", 'r:UTF-8', &:read) bundle = FHIR.from_contents(json) if bundle.resourceType == 'Group' @@ -151,23 +221,44 @@ patient_bundle_absent_name = DataScript::Modifications.modify!(selections, RAND_SEED) tik = Time.now.to_i puts " Modified patients (#{DataScript::TimeUtilities.pretty(tik - tok)})." -group = selections.pop + +puts 'Prefilter constraint testing...' +if constraints.satisfied?(selections) + puts ' All constraints satisfied.' +else + error(" #{constraints.violations.length} remaining constraints violated: #{constraints.violations}") +end +profiles_present = constraints.profiles_present(selections) +profiles_missing = DataScript::Constraints::REQUIRED_PROFILES - profiles_present +if profiles_missing.empty? + puts ' All profiles present.' +else + error(" Missing #{profiles_missing.length} profiles:") + profiles_missing.each {|p| error(" * #{p}")} +end + +puts 'Filtering selected patient data...' +DataScript::Filter.filter!(selections) +tok = Time.now.to_i +puts " Filtered data (#{DataScript::TimeUtilities.pretty(tok - tik)})." puts 'Final constraint testing...' if constraints.satisfied?(selections) puts ' All constraints satisfied.' else - puts " #{constraints.violations.length} remaining constraints violated: #{constraints.violations}" + error(" #{constraints.violations.length} remaining constraints violated: #{constraints.violations}") end profiles_present = constraints.profiles_present(selections) profiles_missing = DataScript::Constraints::REQUIRED_PROFILES - profiles_present if profiles_missing.empty? puts ' All profiles present.' else - puts " Missing #{profiles_missing.length} profiles: #{profiles_missing}" + error(" Missing #{profiles_missing.length} profiles:") + profiles_missing.each {|p| error(" * #{p}")} end -# Add the Group back +# Add the final Group +group = DataScript::Filter.create_group(selections) selections << group # Remove the patient with primitive extensions # because we need to write out their JSON separately. @@ -180,9 +271,14 @@ tik = Time.now.to_i output_data = 'output/data' output_validation = 'output/validation' + puts "Overwriting selections into ./#{output_data}" Dir.mkdir(output_data) unless File.exists?(output_data) FileUtils.rm Dir.glob("./#{output_data}/*.json") + +Dir.mkdir(output_validation) unless File.exists?(output_validation) +FileUtils.rm Dir.glob("./#{output_validation}/*.txt") + selections.each do |bundle| if bundle.resourceType == 'Bundle' id = bundle.entry.first.resource.id @@ -192,6 +288,7 @@ filename = "#{output_data}/#{id}.json" file = File.open(filename,'w:UTF-8') json_string = bundle.to_json + DataScript::Modifications.questionnaire_response_primitive_extension(json_string) if VERSION==5 && json_string.include?(DataScript::Modifications::QUESTIONNAIRE_PRAPARE) # json_string.gsub!('"value": "DATAABSENTREASONEXTENSIONGOESHERE"', "\"_value\": { \"extension\": [ #{DataScript::Modifications.data_absent_reason.to_json} ] }") file.write( json_string ) file.close @@ -220,7 +317,7 @@ # run FHIR validator on output puts 'Running FHIR validator on output.' validation_file = "#{output_validation}/#{patient_bundle_absent_name.entry.first.resource.id}.txt" - system( "java -jar lib/org.hl7.fhir.validator.jar #{filename} -version 4.0.1 -ig hl7.fhir.us.core#3.1.0 > #{validation_file}" ) + validate(filename, validation_file) end tok = Time.now.to_i @@ -265,10 +362,7 @@ # Validating tik = Time.now.to_i -output_validation = 'output/validation' puts "Validating... Output logged in ./#{output_validation}" -Dir.mkdir(output_validation) unless File.exists?(output_validation) -FileUtils.rm Dir.glob("./#{output_validation}/*.txt") selections.each do |bundle| if bundle.resourceType == 'Bundle' id = bundle.entry.first.resource.id @@ -278,14 +372,14 @@ # run FHIR validator on output filename = "#{output_data}/#{id}.json" validation_file = "#{output_validation}/#{id}.txt" - system( "java -jar lib/org.hl7.fhir.validator.jar #{filename} -version 4.0.1 -ig hl7.fhir.us.core > #{validation_file}" ) + validate(filename, validation_file) end if patient_bundle_absent_name filename = "#{output_data}/#{patient_bundle_absent_name.entry.first.resource.id}.json" # run FHIR validator on output validation_file = "#{output_validation}/#{patient_bundle_absent_name.entry.first.resource.id}.txt" - system( "java -jar lib/org.hl7.fhir.validator.jar #{filename} -version 4.0.1 -ig hl7.fhir.us.core > #{validation_file}" ) + validate(filename, validation_file) end tok = Time.now.to_i puts " Validated #{selections.length + (patient_bundle_absent_name ? 1 : 0)} files (#{DataScript::TimeUtilities.pretty(tok - tik)})."