001package org.hl7.fhir.convertors.misc.utg; 002 003import ca.uhn.fhir.model.api.TemporalPrecisionEnum; 004import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50; 005import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; 006import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; 007import org.hl7.fhir.exceptions.FHIRException; 008import org.hl7.fhir.r5.formats.IParser.OutputStyle; 009import org.hl7.fhir.r5.formats.JsonParser; 010import org.hl7.fhir.r5.formats.XmlParser; 011import org.hl7.fhir.r5.model.*; 012import org.hl7.fhir.r5.model.Provenance.ProvenanceAgentComponent; 013import org.hl7.fhir.r5.utils.ToolingExtensions; 014import org.hl7.fhir.utilities.Utilities; 015import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager; 016import org.hl7.fhir.utilities.npm.NpmPackage; 017import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation; 018import org.hl7.fhir.utilities.npm.ToolsVersion; 019 020import java.io.File; 021import java.io.FileInputStream; 022import java.io.FileOutputStream; 023import java.io.IOException; 024import java.net.URISyntaxException; 025import java.text.ParseException; 026import java.util.*; 027 028public class UTGVersionSorter { 029 030 private final Date runTime = new Date(); 031 private FilesystemPackageCacheManager pcm; 032 033 public static void main(String[] args) throws FHIRException, IOException, ParseException, URISyntaxException { 034 new UTGVersionSorter().execute("C:\\work\\org.hl7.fhir.igs\\UTG\\input\\sourceOfTruth"); 035 } 036 037 private void execute(String source) throws IOException { 038 List<CanonicalResourceAnalysis> list = new ArrayList<>(); 039 System.out.println("Loading UTG"); 040 loadFromSource(list, new File(source)); 041 042 Map<String, CanonicalResource> r2 = loadPackageR2("hl7.fhir.r2.core"); 043 Map<String, CanonicalResource> r3 = loadPackageR3("hl7.fhir.r3.core"); 044 Map<String, CanonicalResource> r4 = loadPackageR4("hl7.fhir.r4.core"); 045 046 System.out.println("Processing"); 047 for (CanonicalResourceAnalysis cr : list) { 048 cr.analyse(r2, r3, r4); 049 } 050 051 Bundle b = (Bundle) new JsonParser().parse(new FileInputStream("C:\\work\\org.hl7.fhir.igs\\UTG\\input\\sourceOfTruth\\history\\utgrel1hx-1-0-6.json")); 052 053 System.out.println("Summary"); 054 for (CanonicalResourceAnalysis cr : list) { 055 System.out.println(cr.summary()); 056 Provenance p = new Provenance(); 057 b.addEntry().setResource(p).setFullUrl("http://terminology.hl7.org/fhir/Provenance/fhir-1.0.51-" + cr.getId()); 058 p.setId("hx-fhir-1.0.51-" + cr.getId()); 059 p.addTarget().setReference(cr.fhirType() + "/" + cr.getId()); 060 p.getOccurredPeriod().setEnd(runTime, TemporalPrecisionEnum.DAY); 061 p.setRecorded(runTime); 062 p.addAuthorization().getConcept().setText("Reset Version after migration to UTG").addCoding("http://terminology.hl7.org/CodeSystem/v3-ActReason", "METAMGT", null); 063 p.getActivity().addCoding("http://terminology.hl7.org/CodeSystem/v3-DataOperation", "UPDATE", null); 064 ProvenanceAgentComponent pa = p.addAgent(); 065 pa.getType().addCoding("http://terminology.hl7.org/CodeSystem/provenance-participant-type", "author", null); 066 pa.getWho().setDisplay("Grahame Grieve"); 067 pa = p.addAgent(); 068 pa.getType().addCoding("http://terminology.hl7.org/CodeSystem/provenance-participant-type", "custodian", null); 069 pa.getWho().setDisplay("Vocabulary WG"); 070 CanonicalResource res = cr.resource; 071 res.setVersion(cr.recommendation); 072 new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(cr.filename), res); 073 } 074 System.out.println(); 075 new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream("C:\\work\\org.hl7.fhir.igs\\UTG\\input\\sourceOfTruth\\history\\utgrel1hx-1-0-6.json"), b); 076 System.out.println("Done"); 077 } 078 079 private Map<String, CanonicalResource> loadPackageR2(String id) throws IOException { 080 Map<String, CanonicalResource> res = new HashMap<>(); 081 if (pcm == null) { 082 pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); 083 } 084 System.out.println("Load " + id); 085 NpmPackage npm = pcm.loadPackage(id); 086 for (PackageResourceInformation p : npm.listIndexedResources("CodeSystem", "ValueSet")) { 087 CanonicalResource r = (CanonicalResource) VersionConvertorFactory_10_50.convertResource(new org.hl7.fhir.dstu2.formats.JsonParser().parse(npm.load(p))); 088 res.put(r.getUrl(), r); 089 } 090 return res; 091 } 092 093 private Map<String, CanonicalResource> loadPackageR3(String id) throws IOException { 094 Map<String, CanonicalResource> res = new HashMap<>(); 095 if (pcm == null) { 096 pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); 097 } 098 System.out.println("Load " + id); 099 NpmPackage npm = pcm.loadPackage(id); 100 for (PackageResourceInformation p : npm.listIndexedResources("CodeSystem", "ValueSet")) { 101 CanonicalResource r = (CanonicalResource) VersionConvertorFactory_30_50.convertResource(new org.hl7.fhir.dstu3.formats.JsonParser().parse(npm.load(p))); 102 res.put(r.getUrl(), r); 103 } 104 return res; 105 } 106 107 private Map<String, CanonicalResource> loadPackageR4(String id) throws IOException { 108 Map<String, CanonicalResource> res = new HashMap<>(); 109 if (pcm == null) { 110 pcm = new FilesystemPackageCacheManager(true, ToolsVersion.TOOLS_VERSION); 111 } 112 System.out.println("Load " + id); 113 NpmPackage npm = pcm.loadPackage(id); 114 for (PackageResourceInformation p : npm.listIndexedResources("CodeSystem", "ValueSet")) { 115 CanonicalResource r = (CanonicalResource) VersionConvertorFactory_40_50.convertResource(new org.hl7.fhir.r4.formats.JsonParser().parse(npm.load(p))); 116 res.put(r.getUrl(), r); 117 } 118 return res; 119 } 120 121 private void loadFromSource(List<CanonicalResourceAnalysis> list, File source) { 122 for (File f : source.listFiles()) { 123 if (f.isDirectory()) { 124 loadFromSource(list, f); 125 } else if (f.getName().endsWith(".xml")) { 126 try { 127 Resource r = new XmlParser().parse(new FileInputStream(f)); 128 if (r instanceof CanonicalResource) { 129 CanonicalResource cr = (CanonicalResource) r; 130 cr.setUserData("path", f.getAbsolutePath()); 131 if (cr.hasVersion() && cr.getVersion().startsWith("4.") && !Utilities.existsInList(cr.getId(), "v3-DataOperation", "v3-KnowledgeSubjectObservationValue")) { 132 list.add(new CanonicalResourceAnalysis(cr, f.getAbsolutePath())); 133 } 134 } 135 } catch (Exception e) { 136 System.out.println(f.getAbsolutePath() + " not a resource? " + e.getMessage()); 137 } 138 } 139 } 140 141 } 142 143 public class CanonicalResourceAnalysis { 144 145 private final CanonicalResource resource; 146 private final String filename; 147 private CanonicalResource r2; 148 private CanonicalResource r3; 149 private CanonicalResource r4; 150 private String fmm; 151 private boolean normative; 152 private String recommendation; 153 154 public CanonicalResourceAnalysis(CanonicalResource cr, String filename) { 155 this.resource = cr; 156 this.filename = filename; 157 } 158 159 public String summary() { 160// return "Relevant: "+resource.getUrl()+" [r2: "+r2Ver+"/"+r2Fmm+"]"+" [r3: "+r3Ver+"/"+r3Fmm+"]"+" [r4: "+r4Ver+"/"+r4Fmm+"/"+r4Normative+"] ---> "+recommendation; 161 return resource.getUrl() + " ---> " + recommendation + " in " + filename; 162 } 163 164 public void analyse(Map<String, CanonicalResource> r2l, Map<String, CanonicalResource> r3l, Map<String, CanonicalResource> r4l) { 165 r2 = findMatch(r2l); 166 r3 = findMatch(r3l); 167 r4 = findMatch(r4l); 168 169 fmm = r4 != null ? ToolingExtensions.readStringExtension(r4, ToolingExtensions.EXT_FMM_LEVEL) : null; 170 normative = (r4 != null) && ToolingExtensions.readStringExtension(r4, ToolingExtensions.EXT_NORMATIVE_VERSION) != null; 171 if (normative) { 172 recommendation = "1.0.0"; 173 } else if (Utilities.existsInList(fmm, "3", "4", "5")) { 174 recommendation = "0.5.0"; 175 } else { 176 int i = 1; 177 if (r2 != null && r3 != null && !match(r2, r3, r2l, r3l)) { 178 i++; 179 } 180 if (r3 != null && r4 != null && !match(r3, r4, r3l, r4l)) { 181 i++; 182 } 183 recommendation = "0." + i + ".0"; 184 } 185 } 186 187 private boolean match(CanonicalResource l, CanonicalResource r, Map<String, CanonicalResource> ll, Map<String, CanonicalResource> rl) { 188 if (l instanceof CodeSystem && r instanceof CodeSystem) { 189 return matchCS((CodeSystem) l, (CodeSystem) r); 190 } else if (l instanceof ValueSet && r instanceof ValueSet) { 191 return matchVS((ValueSet) l, (ValueSet) r, ll, rl); 192 } else { 193 return false; 194 } 195 } 196 197 private boolean matchVS(ValueSet l, ValueSet r, Map<String, CanonicalResource> ll, Map<String, CanonicalResource> rl) { 198 if (l.getCompose().getInclude().size() == 1 && l.getCompose().getExclude().isEmpty() && l.getCompose().getIncludeFirstRep().hasSystem() && !l.getCompose().getIncludeFirstRep().hasConcept() && !l.getCompose().getIncludeFirstRep().hasFilter() && 199 r.getCompose().getInclude().size() == 1 && r.getCompose().getExclude().isEmpty() && r.getCompose().getIncludeFirstRep().hasSystem() && !r.getCompose().getIncludeFirstRep().hasConcept() && !r.getCompose().getIncludeFirstRep().hasFilter()) { 200 CodeSystem lc = (CodeSystem) ll.get(l.getCompose().getIncludeFirstRep().getSystem()); 201 CodeSystem rc = (CodeSystem) rl.get(l.getCompose().getIncludeFirstRep().getSystem()); 202 if (lc != null && rc != null) { 203 return matchCS(lc, rc); 204 } 205 } 206 return false; 207 } 208 209 private boolean matchCS(CodeSystem l, CodeSystem r) { 210 return Base.compareDeep(l.getConcept(), r.getConcept(), false); 211 } 212 213 public CanonicalResource findMatch(Map<String, CanonicalResource> r2) { 214 CanonicalResource r = r2.get(resource.getUrl()); 215 if (r == null) { 216 r = r2.get(resource.getUrl().replaceAll("http://terminology.hl7.org/", "http://hl7.org/fhir/")); 217 } 218 if (r == null) { 219 r = r2.get(resource.getUrl().replaceAll("http://terminology.hl7.org/CodeSystem", "http://hl7.org/fhir/")); 220 } 221 return r; 222 } 223 224 public String getId() { 225 return resource.getId(); 226 } 227 228 public String fhirType() { 229 return resource.fhirType(); 230 } 231 232 } 233 234 235}