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