Skip to content

Commit

Permalink
Merge pull request #1015 from HL7/2025-01-gg-archetypes
Browse files Browse the repository at this point in the history
2025 01 gg archetypes
  • Loading branch information
grahamegrieve authored Jan 6, 2025
2 parents f06b386 + 1b9d4f7 commit 5f03f07
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import javax.xml.parsers.ParserConfigurationException;

import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Bundle.BundleType;
Expand All @@ -36,6 +37,7 @@
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.xml.XMLUtil;
import org.openehr.referencemodels.BuiltinReferenceModels;
Expand Down Expand Up @@ -72,23 +74,95 @@ public class ArchetypeImporter {
private StructureDefinition sd;
private Bundle bnd;

protected ArchetypeImporter(IWorkerContext context, String canonicalBase) throws ParserConfigurationException, SAXException, IOException {
public static class ProcessedArchetype {
private Archetype archetype;
private StructureDefinition sd;
private Bundle bnd;
private String source;
private String sourceName;

protected ProcessedArchetype(String source,String sourceName, Archetype archetype, Bundle bnd, StructureDefinition sd) {
super();
this.source = source;
this.sourceName = sourceName;
this.archetype = archetype;
this.sd = sd;
this.bnd = bnd;
}
public String getSource() {
return source;
}
public String getSourceName() {
return sourceName;
}
public Archetype getArchetype() {
return archetype;
}
public StructureDefinition getSd() {
return sd;
}
public Bundle getBnd() {
return bnd;
}

}
public ArchetypeImporter(IWorkerContext context, String canonicalBase) throws ParserConfigurationException, SAXException, IOException {
super();
this.context = context;
this.canonicalBase = canonicalBase;
// todo: check openehr-base is loaded, and if it's not, load it
// todo: load this from the context.binaries
terminology = XMLUtil.parseFileToDom("/Users/grahamegrieve/Downloads/openehr_terminology.xml").getDocumentElement();
byte[] tf = context.getBinaryForKey("openehr_terminology.xml");
if (tf == null) {
// hack temp workaround
tf = TextFile.fileToBytes("/Users/grahamegrieve/Downloads/openehr_terminology.xml");
}
terminology = XMLUtil.parseToDom(tf).getDocumentElement();
}


public void checkArchetype(byte[] content, String name) throws ParserConfigurationException, SAXException, IOException, ADLParseException {
checkArchetype(new ByteArrayInputStream(content), name);
}

public Bundle importArchetype(byte[] content, boolean adl14) throws ParserConfigurationException, SAXException, IOException, ADLParseException {
return importArchetype(new ByteArrayInputStream(content), adl14);
public void checkArchetype(InputStream stream, String name) throws ParserConfigurationException, SAXException, IOException, ADLParseException {
byte[] cnt = TextFile.streamToBytes(stream);
try {
archetype = load20(new ByteArrayInputStream(cnt));
} catch (Exception e20) {
try {
archetype = load14(new ByteArrayInputStream(cnt));
} catch (Exception e14) {
if (e20.getMessage().equals(e14.getMessage())) {
throw new FHIRException("Error reading "+name+": "+e20.getMessage(), e20);
} else {
throw new FHIRException("Error reading "+name+". ADL 1.4: "+e14.getMessage()+"; ADL 2: "+e20.getMessage(), e20);
}
}
}
}

public ProcessedArchetype importArchetype(byte[] content, String name) throws ParserConfigurationException, SAXException, IOException, ADLParseException {
return importArchetype(new ByteArrayInputStream(content), name);
}

public Bundle importArchetype(InputStream stream, boolean adl14) throws ParserConfigurationException, SAXException, IOException, ADLParseException {
public ProcessedArchetype importArchetype(InputStream stream, String name) throws ParserConfigurationException, SAXException, IOException, ADLParseException {
atMap.clear();

archetype = adl14 ? load14(stream) : load20(stream);

byte[] cnt = TextFile.streamToBytes(stream);
try {
archetype = load20(new ByteArrayInputStream(cnt));
} catch (Exception e20) {
try {
archetype = load14(new ByteArrayInputStream(cnt));
} catch (Exception e14) {
if (e20.getMessage().equals(e14.getMessage())) {
throw new FHIRException("Error reading "+name+": "+e20.getMessage(), e20);
} else {
throw new FHIRException("Error reading "+name+". ADL 1.4: "+e14.getMessage()+"; ADL 2: "+e20.getMessage(), e20);
}
}
}

bnd = new Bundle();
bnd.setType(BundleType.COLLECTION);
Expand Down Expand Up @@ -148,7 +222,7 @@ public Bundle importArchetype(InputStream stream, boolean adl14) throws ParserCo
List<ElementDefinition> defns = sd.getDifferential().getElement();
processDefinition(defns, null, defn, baseType, null, baseType, defn.getNodeId());

return bnd;
return new ProcessedArchetype(new String(cnt), name, archetype, bnd, sd);
}

private Archetype load20(InputStream stream) throws ADLParseException, IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import javax.xml.parsers.ParserConfigurationException;

import org.hl7.fhir.igtools.openehr.ArchetypeImporter.ProcessedArchetype;
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.model.Bundle;
Expand All @@ -22,9 +23,9 @@ public static void main(String[] args) throws Exception {

public void test() throws FileNotFoundException, ADLParseException, IOException, ParserConfigurationException, SAXException {
ArchetypeImporter ai = new ArchetypeImporter(null, "http://openehr.org/fhir/uv/test");
Bundle bnd = ai.importArchetype(new FileInputStream("/Users/grahamegrieve/Downloads/openEHR-EHR-OBSERVATION.blood_pressure.v2.adl"), true);
ProcessedArchetype pa = ai.importArchetype(new FileInputStream("/Users/grahamegrieve/Downloads/openEHR-EHR-OBSERVATION.blood_pressure.v2.adl"), "openEHR-EHR-OBSERVATION.blood_pressure.v2.adl");
System.out.println();
String json = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(bnd);
String json = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeString(pa.getBnd());
TextFile.stringToFile(json, "/Users/grahamegrieve/temp/igs/FHIR-sample-ig#master/input/resources/Bundle-openEHR-EHR-OBSERVATION.blood-pressure.v2.json");
System.out.println("Done");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,7 @@ private void processValidatorPack(File f) throws IOException {
String destFile = dest != null ? dest : Utilities.path(Utilities.getDirectoryForFile(f.getAbsolutePath()), "package.tgz");
String url = Utilities.noString(website) ? canonical : website;
NPMPackageGenerator npm = new NPMPackageGenerator(destFile, canonical, url, PackageType.IG, ig, new Date(), false);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
new JsonParser().setOutputStyle(OutputStyle.NORMAL).compose(bs, ig);
npm.addFile(Category.OTHER, "ig-r4.jsonX", bs.toByteArray());


npm.addFile(Category.RESOURCE, "ImplementationGuide-"+ig.getId()+".json", compose(ig, version));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.igtools.openehr.ArchetypeImporter;
import org.hl7.fhir.igtools.openehr.ArchetypeImporter.ProcessedArchetype;
import org.hl7.fhir.igtools.publisher.FetchedFile.FetchedBundleType;
import org.hl7.fhir.igtools.publisher.IFetchFile.FetchState;
import org.hl7.fhir.igtools.publisher.comparators.IpaComparator;
Expand Down Expand Up @@ -704,6 +706,7 @@ public enum CacheOption {
private List<String> pagesDirs = new ArrayList<String>();
private List<String> testDirs = new ArrayList<String>();
private List<String> dataDirs = new ArrayList<String>();
private List<String> otherDirs = new ArrayList<String>();
private String tempDir;
private String tempLangDir;
private String outputDir;
Expand Down Expand Up @@ -2877,6 +2880,9 @@ private void initializeFromIg(IniFile ini) throws Exception {
case "path-data":
dataDirs.add(Utilities.path(rootDir, p.getValue()));
break;
case "path-other":
otherDirs.add(Utilities.path(rootDir, p.getValue()));
break;
case "copyrightyear":
copyrightYear = p.getValue();
break;
Expand Down Expand Up @@ -3354,6 +3360,7 @@ else if (vsCache == null) {
igpkp.setAutoPath(true);
}
fetcher.setPkp(igpkp);
fetcher.setContext(context);
template.loadSummaryRows(igpkp.summaryRows());

if (VersionUtilities.isR4Plus(version) && !dependsOnExtensions(sourceIg.getDependsOn()) && !sourceIg.getPackageId().contains("hl7.fhir.uv.extensions")) {
Expand Down Expand Up @@ -5689,16 +5696,74 @@ private boolean loadBundle(String name, boolean needToBuild, FetchedFile igf, St
return changed || needToBuild;
}

private boolean loadArchetype(FetchedFile f, String cause) throws Exception {
ProcessedArchetype pa = new ArchetypeImporter(context, igpkp.getCanonical()).importArchetype(f.getSource(), new File(f.getStatedPath()).getName());
Bundle bnd = pa.getBnd();
pa.getSd().setUserData(UserDataNames.archetypeSource, pa.getSource());
pa.getSd().setUserData(UserDataNames.archetypeName, pa.getSourceName());

f.setBundle(new FetchedResource(f.getName()+" (bundle)"));
f.setBundleType(FetchedBundleType.NATIVE);

boolean changed = noteFile("Bundle/"+bnd.getIdBase(), f);
int i = -1;
for (BundleEntryComponent be : bnd.getEntry()) {
i++;
Resource res = be.getResource();
Element e = new ObjectConverter(context).convert(res);
checkResourceUnique(res.fhirType()+"/"+res.getIdBase(), f.getName(), cause);
FetchedResource r = f.addResource(f.getName()+"["+i+"]");
r.setElement(e);
r.setResource(res);
r.setId(res.getIdBase());

r.setTitle(r.getElement().getChildValue("name"));
igpkp.findConfiguration(f, r);
}
for (FetchedResource r : f.getResources()) {
bndIds.add(r.fhirType()+"/"+r.getId());
ImplementationGuideDefinitionResourceComponent res = findIGReference(r.fhirType(), r.getId());
if (res == null) {
res = publishedIg.getDefinition().addResource();
if (!res.hasName())
if (r.hasTitle())
res.setName(r.getTitle());
else
res.setName(r.getId());
if (!res.hasDescription() && r.getElement().hasChild("description")) {
res.setDescription(r.getElement().getChildValue("description").trim());
}
res.setReference(new Reference().setReference(r.fhirType()+"/"+r.getId()));
}
res.setUserData(UserDataNames.pub_loaded_resource, r);
r.setResEntry(res);
if (r.getResource() instanceof CanonicalResource) {
CanonicalResource cr = (CanonicalResource)r.getResource();
if (!canonicalResources.containsKey(cr.getUrl())) {
canonicalResources.put(cr.getUrl(), r);
if (cr.hasVersion())
canonicalResources.put(cr.getUrl()+"#"+cr.getVersion(), r);
}
}
}
return changed;
}

private boolean loadResources(boolean needToBuild, FetchedFile igf) throws Exception { // igf is not currently used, but it was about relative references?
List<FetchedFile> resources = fetcher.scan(sourceDir, context, igpkp.isAutoPath());
for (FetchedFile ff : resources) {
ff.start("loadResources");
try {
if (!ff.matches(igf) && !isBundle(ff)) {
needToBuild = loadResource(needToBuild, ff, "scan folder "+Utilities.getDirectoryForFile(ff.getStatedPath()));
if (ff.getContentType().equals("adl")) {
loadArchetype(ff, "scan folder "+Utilities.getDirectoryForFile(ff.getStatedPath()));
needToBuild = true;
} else {
try {
if (!ff.matches(igf) && !isBundle(ff)) {
needToBuild = loadResource(needToBuild, ff, "scan folder "+Utilities.getDirectoryForFile(ff.getStatedPath()));
}
} finally {
ff.finish("loadResources");
}
} finally {
ff.finish("loadResources");
}
}
return needToBuild;
Expand Down Expand Up @@ -6630,7 +6695,7 @@ private ImplementationGuideDefinitionResourceComponent findIGReference(String ty
}
return null;
}

private Element loadFromMap(FetchedFile file) throws Exception {
if (!VersionUtilities.isR4Plus(context.getVersion())) {
throw new Error("Loading Map Files is not supported for version "+VersionUtilities.getNameForVersion(context.getVersion()));
Expand Down Expand Up @@ -8083,6 +8148,18 @@ private void generate() throws Exception {
for (String t : testDirs) {
addTestDir(new File(t), t);
}
for (String n : otherDirs) {
File f = new File(n);
if (f.exists()) {
for (File ff : f.listFiles()) {
if (!SimpleFetcher.isIgnoredFile(f.getName())) {
npm.addFile(Category.OTHER, ff.getName(), TextFile.fileToBytes(ff.getAbsolutePath()));
}
}
} else {
logMessage("Other Directory not found: "+n);
}
}
npm.finish();
if (r4tor4b.canBeR4() && r4tor4b.canBeR4B()) {
try {
Expand Down Expand Up @@ -8339,11 +8416,7 @@ private void updateImplementationGuide() throws Exception {
}
r.setResource(publishedIg);
r.setElement(convertToElement(r, publishedIg));

ByteArrayOutputStream bs = new ByteArrayOutputStream();
new org.hl7.fhir.r4.formats.JsonParser().compose(bs, VersionConvertorFactory_40_50.convertResource(publishedIg));
npm.addFile(Category.OTHER, "ig-r4.jsonX", bs.toByteArray());


for (ImplementationGuideDefinitionResourceComponent res : publishedIg.getDefinition().getResource()) {
FetchedResource rt = null;
for (FetchedFile tf : fileList) {
Expand Down Expand Up @@ -12335,7 +12408,10 @@ private byte[] saveNativeResourceOutputs(FetchedFile f, FetchedResource r) throw
Element eNN = element;
jp.compose(element, bsj, OutputStyle.NORMAL, igpkp.getCanonical());
if (!r.isCustomResource()) {
npm.addFile(isExample(f,r ) ? Category.EXAMPLE : Category.RESOURCE, element.fhirType()+"-"+r.getId()+".json", bsj.toByteArray());
npm.addFile(isExample(f,r ) ? Category.EXAMPLE : Category.RESOURCE, element.fhirType()+"-"+r.getId()+".json", bsj.toByteArray());
if (r.getResource() != null && r.getResource().hasUserData(UserDataNames.archetypeSource)) {
npm.addFile(Category.ADL, r.getResource().getUserString(UserDataNames.archetypeName), r.getResource().getUserString(UserDataNames.archetypeSource).getBytes(StandardCharsets.UTF_8));
}
} else if ("StructureDefinition".equals(r.fhirType())) {
npm.addFile(Category.RESOURCE, element.fhirType()+"-"+r.getId()+".json", bsj.toByteArray());
StructureDefinition sdt = (StructureDefinition) r.getResource().copy();
Expand Down Expand Up @@ -13172,6 +13248,10 @@ private void generateOutputsStructureDefinition(FetchedFile f, FetchedResource r
fragment("StructureDefinition-"+prefixForContainer+sd.getId()+"-eview"+langSfx, sdr.eview(igpkp.getDefinitionsName(r), otherFilesRun, tabbedSnapshots, StructureDefinitionRendererMode.SUMMARY, false), f.getOutputNames(), r, vars, null, start, "eview", "StructureDefinition");
fragment("StructureDefinition-"+prefixForContainer+sd.getId()+"-eview-all"+langSfx, sdr.eview(igpkp.getDefinitionsName(r), otherFilesRun, tabbedSnapshots, StructureDefinitionRendererMode.SUMMARY, true), f.getOutputNames(), r, vars, null, start, "eview", "StructureDefinition");
}
if (igpkp.wantGen(r, "adl") && sd.hasUserData(UserDataNames.archetypeSource)) {
long start = System.currentTimeMillis();
fragment("StructureDefinition-"+prefixForContainer+sd.getId()+"-adl"+langSfx, sdr.adl(), f.getOutputNames(), r, vars, null, start, "adl", "StructureDefinition");
}
if (igpkp.wantGen(r, "diff")) {
long start = System.currentTimeMillis();
fragment("StructureDefinition-"+prefixForContainer+sd.getId()+"-diff"+langSfx, sdr.diff(igpkp.getDefinitionsName(r), otherFilesRun, tabbedSnapshots, StructureDefinitionRendererMode.SUMMARY, false), f.getOutputNames(), r, vars, null, start, "diff", "StructureDefinition");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,8 +467,6 @@ private void processFileSame(NPMPackageGenerator gen, String folder, String file
// System.out.println("** Exclude "+res.fhirType()+"/"+res.getId()+" from same version");
}
}
} else if (filename.equals("ig-r4.json") || filename.equals("ig-r4.jsonX")) {
gen.addFile(folder, filename, updateIGR4(content, ver, pver));
} else if (filename.equals("spec.internals")) {
gen.addFile(folder, filename, updateSpecInternals(content, ver, pver));
} else {
Expand Down
Loading

0 comments on commit 5f03f07

Please sign in to comment.