/*
* Licensed under the Apache License, Version 2.0 (the "License");
*
* You may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributions from 2013-2017 where performed either by US government
* employees, or under US Veterans Health Administration contracts.
*
* US Veterans Health Administration contributions by government employees
* are work of the U.S. Government and are not subject to copyright
* protection in the United States. Portions contributed by government
* employees are USGovWork (17USC §105). Not subject to copyright.
*
* Contribution by contractors to the US Veterans Health Administration
* during this period are contractually contributed under the
* Apache License, Version 2.0.
*
* See: https://www.usa.gov/government-works
*
* Contributions prior to 2013:
*
* Copyright (C) International Health Terminology Standards Development Organisation.
* Licensed under the Apache License, Version 2.0.
*
*/
package sh.isaac.convert.rxnorm.standard;
//~--- JDK imports ------------------------------------------------------------
import java.beans.PropertyVetoException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
//~--- non-JDK imports --------------------------------------------------------
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import sh.isaac.MetaData;
import sh.isaac.api.Get;
import sh.isaac.api.State;
import sh.isaac.api.chronicle.LatestVersion;
import sh.isaac.api.component.sememe.SememeChronology;
import sh.isaac.api.component.sememe.version.DescriptionSememe;
import sh.isaac.api.component.sememe.version.StringSememe;
import sh.isaac.api.component.sememe.version.dynamicSememe.DynamicSememeDataType;
import sh.isaac.api.util.UuidT3Generator;
import sh.isaac.convert.rxnorm.propertyTypes.PT_Annotations;
import sh.isaac.convert.rxnorm.propertyTypes.ValuePropertyPairWithSAB;
import sh.isaac.converters.sharedUtils.ComponentReference;
import sh.isaac.converters.sharedUtils.ConsoleUtil;
import sh.isaac.converters.sharedUtils.ConverterBaseMojo;
import sh.isaac.converters.sharedUtils.IBDFCreationUtility;
import sh.isaac.converters.sharedUtils.IBDFCreationUtility.DescriptionType;
import sh.isaac.converters.sharedUtils.propertyTypes.BPT_Annotations;
import sh.isaac.converters.sharedUtils.propertyTypes.BPT_Associations;
import sh.isaac.converters.sharedUtils.propertyTypes.BPT_Descriptions;
import sh.isaac.converters.sharedUtils.propertyTypes.BPT_Relations;
import sh.isaac.converters.sharedUtils.propertyTypes.Property;
import sh.isaac.converters.sharedUtils.propertyTypes.PropertyAssociation;
import sh.isaac.converters.sharedUtils.propertyTypes.PropertyType;
import sh.isaac.converters.sharedUtils.propertyTypes.ValuePropertyPair;
import sh.isaac.converters.sharedUtils.sql.TableDefinition;
import sh.isaac.converters.sharedUtils.stats.ConverterUUID;
import sh.isaac.converters.sharedUtils.umlsUtils.AbbreviationExpansion;
import sh.isaac.converters.sharedUtils.umlsUtils.RRFDatabaseHandle;
import sh.isaac.converters.sharedUtils.umlsUtils.Relationship;
import sh.isaac.converters.sharedUtils.umlsUtils.UMLSFileReader;
import sh.isaac.converters.sharedUtils.umlsUtils.ValuePropertyPairWithAttributes;
import sh.isaac.converters.sharedUtils.umlsUtils.propertyTypes.PT_Descriptions;
import sh.isaac.converters.sharedUtils.umlsUtils.propertyTypes.PT_Refsets;
import sh.isaac.converters.sharedUtils.umlsUtils.propertyTypes.PT_Relationship_Metadata;
import sh.isaac.converters.sharedUtils.umlsUtils.propertyTypes.PT_SAB_Metadata;
import sh.isaac.converters.sharedUtils.umlsUtils.rrf.REL;
import sh.isaac.model.configuration.StampCoordinates;
import sh.isaac.rxnorm.rrf.RXNCONSO;
import sh.isaac.rxnorm.rrf.RXNSAT;
//~--- classes ----------------------------------------------------------------
/**
* Loader code to convert RxNorm into the workbench.
*/
@Mojo(
name = "convert-rxnorm-to-ibdf",
defaultPhase = LifecyclePhase.PROCESS_SOURCES
)
public class RxNormMojo
extends ConverterBaseMojo {
/**
* The Constant rxNormName.
*/
public static final String rxNormName = "RxNorm";
/**
* The Constant cpcRefsetConceptKey.
*/
public static final String cpcRefsetConceptKey = "Current Prescribable Content";
//~--- fields --------------------------------------------------------------
/**
* The table prefix.
*/
private final String tablePrefix = "RXN";
/**
* The sct sab.
*/
private final String sctSab = "SNOMEDCT_US";
/**
* The name to relationship map.
*/
private HashMap<String, Relationship> nameToRel = new HashMap<>();
/**
* The semantic types.
*/
private final HashMap<String, UUID> semanticTypes = new HashMap<>();
/**
* The loaded relationships.
*/
private final HashSet<UUID> loadedRels = new HashSet<>();
/**
* The skipped relationships.
*/
private final HashSet<UUID> skippedRels = new HashSet<>();
/**
* The map to isa.
*/
private final HashMap<String, Boolean> mapToIsa =
new HashMap<>(); // FSN, true or false - true for rel only, false for a rel and association representation
/**
* The sct id to UUID.
*/
private final HashMap<Long, UUID> sctIdToUUID = new HashMap<>(); // A map of real (found) SCTIDs to their concept UUID
/**
* The cui to SCT ID.
*/
private final HashMap<String, Long> cuiToSCTID =
new HashMap<>(); // Map CUI to SCTID for the real sctIds to UUIDs found above
/**
* The skipped rel for not matching CUI filter.
*/
private final AtomicInteger skippedRelForNotMatchingCUIFilter = new AtomicInteger();
/**
* The date parse.
*/
// Format to parse 01/28/2010
private final SimpleDateFormat dateParse = new SimpleDateFormat("MM/dd/yyyy");
/**
* The s types.
*/
private HashMap<String, UUID> sTypes;
/**
* The suppress.
*/
private HashMap<String, UUID> suppress;
/**
* The source restriction levels.
*/
private HashMap<String, UUID> sourceRestrictionLevels;
/**
* The pt UMLS attributes.
*/
private PropertyType ptUMLSAttributes;
/**
* The pt SABs.
*/
private PropertyType ptSABs;
/**
* The pt relationship metadata.
*/
private PropertyType ptRelationshipMetadata;
/**
* The pt descriptions.
*/
private PropertyType ptDescriptions; // TODO get SAB types for these, annotate
/**
* The pt associations.
*/
private BPT_Associations ptAssociations; // TODO get SAB types for these, annotate
/**
* The pt relationships.
*/
private BPT_Relations ptRelationships; // TODO get SAB types for these, annotate
/**
* The pt term attributes.
*/
private PropertyType ptTermAttributes; // TODO get SAB types for these, annotate
/**
* The pt refsets.
*/
private PT_Refsets ptRefsets;
/**
* The import util.
*/
private IBDFCreationUtility importUtil;
/**
* The db.
*/
private RRFDatabaseHandle db;
/**
* The meta data root.
*/
private ComponentReference metaDataRoot;
/**
* The abbreviation expansions.
*/
private HashMap<String, AbbreviationExpansion> abbreviationExpansions;
/**
* The all CUI refset concept.
*/
private ComponentReference allCUIRefsetConcept;
/**
* The cpc refset concept.
*/
private ComponentReference cpcRefsetConcept;
/**
* The has TTY type.
*/
private PreparedStatement semanticTypeStatement, descSat, cuiRelStatementForward, cuiRelStatementBackward,
satRelStatement, hasTTYType;
/**
* The allowed CU is for SA bs.
*/
private HashSet<String> allowedCUIsForSABs;
/**
* An optional list of TTY types which should be included. If left blank, we create concepts from all CUI's that are
* in the SAB RxNorm. If provided, we only create concepts where the RxCUI has an entry with a TTY that matches one
* of the TTY's provided here
*/
@Parameter(required = false)
protected List<String> ttyRestriction;
/**
* An optional list of SABs which should be included. We always include the SAB RXNORM. Use this parameter to specify
* others to include. If SNOMEDCT_US is included, then a snomed CT ibdf file must be present - snomed CT is not
* loaded from RxNorm, but rather, linked to the provided SCT IBDF file.
*/
@Parameter(required = false)
protected List<String> sabsToInclude;
/**
* The link snomed CT.
*/
private boolean linkSnomedCT;
//~--- methods -------------------------------------------------------------
/**
* Execute.
*
* @throws MojoExecutionException the mojo execution exception
*/
@Override
public void execute()
throws MojoExecutionException {
try {
super.execute();
init();
this.allCUIRefsetConcept = ComponentReference.fromConcept(
this.ptRefsets.getProperty(this.ptRefsets.CUI_CONCEPTS.getSourcePropertyNameFSN())
.getUUID());
this.cpcRefsetConcept = ComponentReference.fromConcept(this.ptRefsets.getProperty(cpcRefsetConceptKey)
.getUUID());
this.semanticTypeStatement = this.db.getConnection()
.prepareStatement("select TUI, ATUI, CVF from RXNSTY where RXCUI = ?");
// we always grab the description type NDC if present, even if NDC doesn't come from a SAB we are including.
this.descSat = this.db.getConnection()
.prepareStatement("select * from RXNSAT where RXCUI = ? and RXAUI = ? and (" +
createSabQueryPart("",
false) + " or ATN='NDC')");
// UMLS and RXNORM do different things with rels - UMLS never has null CUI's, while RxNorm always has null CUI's (when AUI is specified)
// Also need to join back to MRCONSO to make sure that the target concept is one that we will load with the SAB filter in place.
this.cuiRelStatementForward = this.db.getConnection()
.prepareStatement(
"SELECT distinct r.RXCUI1, r.RXAUI1, r.STYPE1, r.REL, r.RXCUI2, r.RXAUI2, r.STYPE2, " +
"r.RELA, r.RUI, r.SRUI, r.SAB, r.SL, r.DIR, r.RG, r.SUPPRESS, r.CVF from RXNREL as r, RXNCONSO " +
"WHERE RXCUI2 = ? and RXAUI2 is null and " + createSabQueryPart("r.",
this.linkSnomedCT) + " and r.RXCUI1 = RXNCONSO.RXCUI and " + createSabQueryPart("RXNCONSO.",
this.linkSnomedCT));
this.cuiRelStatementBackward = this.db.getConnection()
.prepareStatement(
"SELECT distinct r.RXCUI1, r.RXAUI1, r.STYPE1, r.REL, r.RXCUI2, r.RXAUI2, r.STYPE2, " +
"r.RELA, r.RUI, r.SRUI, r.SAB, r.SL, r.DIR, r.RG, r.SUPPRESS, r.CVF from RXNREL as r, RXNCONSO " +
"WHERE RXCUI1 = ? and RXAUI1 is null and " + createSabQueryPart("r.",
this.linkSnomedCT) + " and r.RXCUI2 = RXNCONSO.RXCUI and " + createSabQueryPart("RXNCONSO.",
this.linkSnomedCT));
int cuiCounter = 0;
final HashSet<String> skippedCUIForNotMatchingCUIFilter;
final ArrayList<RXNCONSO> conceptData;
try (Statement statement = this.db.getConnection().createStatement()) {
final StringBuilder ttyRestrictionQuery = new StringBuilder();
if ((this.ttyRestriction != null) && (this.ttyRestriction.size() > 0)) {
ttyRestrictionQuery.append(" and (");
for (final String s: this.ttyRestriction) {
ttyRestrictionQuery.append("TTY = '");
ttyRestrictionQuery.append(s);
ttyRestrictionQuery.append("' or ");
}
ttyRestrictionQuery.setLength(ttyRestrictionQuery.length() - " or ".length());
ttyRestrictionQuery.append(")");
}
this.allowedCUIsForSABs = new HashSet<>();
try (ResultSet rs = statement.executeQuery("select RXCUI from RXNCONSO where " + createSabQueryPart("",
this.linkSnomedCT) + " " +
ttyRestrictionQuery);) {
while (rs.next()) {
this.allowedCUIsForSABs.add(rs.getString("RXCUI"));
}
}
try (ResultSet rs =
statement.executeQuery(
"select RXCUI, LAT, RXAUI, SAUI, SCUI, SAB, TTY, CODE, STR, SUPPRESS, CVF from RXNCONSO " +
"where " + createSabQueryPart("",
this.linkSnomedCT) + " order by RXCUI")) {
skippedCUIForNotMatchingCUIFilter = new HashSet<>();
conceptData = new ArrayList<>();
while (rs.next()) {
final RXNCONSO current = new RXNCONSO(rs);
if (!this.allowedCUIsForSABs.contains(current.rxcui)) {
skippedCUIForNotMatchingCUIFilter.add(current.rxcui);
continue;
}
if ((conceptData.size() > 0) &&!conceptData.get(0).rxcui.equals(current.rxcui)) {
processCUIRows(conceptData);
if (cuiCounter % 100 == 0) {
ConsoleUtil.showProgress();
}
cuiCounter++;
if (cuiCounter % 10000 == 0) {
ConsoleUtil.println("Processed " + cuiCounter + " CUIs creating " +
this.importUtil.getLoadStats().getConceptCount() + " concepts");
}
conceptData.clear();
}
conceptData.add(current);
}
}
}
// process last
processCUIRows(conceptData);
ConsoleUtil.println("Processed " + cuiCounter + " CUIs creating " +
this.importUtil.getLoadStats().getConceptCount() + " concepts");
ConsoleUtil.println("Skipped " + skippedCUIForNotMatchingCUIFilter.size() +
" concepts for not containing the desired TTY");
ConsoleUtil.println("Skipped " + this.skippedRelForNotMatchingCUIFilter +
" relationships for linking to a concept we didn't include");
this.semanticTypeStatement.close();
this.descSat.close();
this.cuiRelStatementForward.close();
this.cuiRelStatementBackward.close();
finish();
} catch (final Exception e) {
throw new MojoExecutionException("Failure during conversion", e);
} finally {
if (this.db != null) {
try {
this.db.shutdown();
} catch (final SQLException e) {
throw new RuntimeException(e);
}
}
}
}
/**
* The main method.
*
* @param args the arguments
* @throws MojoExecutionException the mojo execution exception
*/
public static void main(String[] args)
throws MojoExecutionException {
final RxNormMojo mojo = new RxNormMojo();
mojo.outputDirectory = new File("../rxnorm-ibdf/rxnorm/target");
mojo.inputFileLocation = new File("../rxnorm-ibdf/rxnorm/target/generated-resources/src");
mojo.converterVersion = "foo";
mojo.converterOutputArtifactVersion = "bar";
mojo.converterSourceArtifactVersion = "foooo";
mojo.converterOutputArtifactId = "rxnorm-ibdf";
mojo.execute();
}
/**
* Adds the relationships.
*
* @param concept the concept
* @param relationships the relationships
* @return the array list
* @throws SQLException the SQL exception
* @throws PropertyVetoException the property veto exception
*/
private ArrayList<UUID> addRelationships(ComponentReference concept,
List<REL> relationships)
throws SQLException,
PropertyVetoException {
final ArrayList<UUID> parents = new ArrayList<>();
for (final REL relationship: relationships) {
relationship.setSourceUUID(concept.getPrimordialUuid());
if (relationship.getSourceAUI() == null) {
if (this.cuiToSCTID.get(relationship.getTargetCUI()) != null) {
if (this.cuiToSCTID.get(relationship.getSourceCUI()) != null) {
// Both source and target are concepts we are linking from SCT. Don't load the rell.
continue;
}
// map to existing target SCT concept
relationship.setTargetUUID(this.sctIdToUUID.get(this.cuiToSCTID.get(relationship.getTargetCUI())));
} else {
// must be a concept we are creating
relationship.setTargetUUID(createCUIConceptUUID(relationship.getTargetCUI()));
}
} else {
throw new RuntimeException("don't yet handle AUI associations");
// relationship.setTargetUUID(createCuiSabCodeConceptUUID(relationship.getRxNormTargetCUI(),
// relationship.getTargetSAB(), relationship.getTargetCode()));
}
// We currently don't check the properties on the (duplicate) inverse rels to make sure they are all present - we assume that they
// created the inverse relationships as an exact copy of the primary rel direction. So, just checking the first rel from our dupe list is good enough
if (isRelPrimary(relationship.getRel(), relationship.getRela())) {
// This can happen when the reverse of the rel equals the rel... sib/sib
if (relCheckIsRelLoaded(relationship)) {
continue;
}
final Property relTypeAsRel = this.ptRelationships.getProperty(((relationship.getRela() == null)
? relationship.getRel()
: relationship.getRela()));
final PropertyAssociation relTypeAsAssn =
(PropertyAssociation) this.ptAssociations.getProperty(((relationship.getRela() == null)
? relationship.getRel()
: relationship.getRela()));
ComponentReference r;
if (relTypeAsRel != null) {
parents.add(relationship.getTargetUUID());
continue;
} else if (relTypeAsAssn != null) {
r = ComponentReference.fromChronology(this.importUtil.addAssociation(concept,
((relationship.getRui() != null)
? ConverterUUID.createNamespaceUUIDFromString("RUI:" + relationship.getRui())
: null),
relationship.getTargetUUID(),
relTypeAsAssn.getUUID(),
State.ACTIVE,
null,
null),
() -> "Association");
} else {
throw new RuntimeException("Unexpected rel handling");
}
// Add the annotations
final HashSet<String> addedRUIs = new HashSet<>();
if (StringUtils.isNotBlank(relationship.getRela())) // we already used rela - annotate with rel.
{
Property genericType = (this.ptAssociations.getProperty(relationship.getRel()) == null)
? this.ptRelationships.getProperty(relationship.getRel())
: this.ptAssociations.getProperty(relationship.getRel());
boolean reversed = false;
if ((genericType == null) && relationship.getRela().equals("mapped_from")) {
// This is to handle non-sensical data in UMLS... they have no consistency in the generic rel they assign - sometimes RB, sometimes RN.
// reverse it - currently, only an issue on 'mapped_from' rels - as the code in Relationship.java has some exceptions for this type.
genericType = (this.ptAssociations.getProperty(reverseRel(relationship.getRel())) == null)
? this.ptRelationships.getProperty(reverseRel(relationship.getRel()))
: this.ptAssociations.getProperty(reverseRel(relationship.getRel()));
reversed = true;
}
this.importUtil.addUUIDAnnotation(r,
genericType.getUUID(),
this.ptUMLSAttributes.getProperty(reversed
? "Generic rel type (inverse)"
: "Generic rel type")
.getUUID());
}
if (StringUtils.isNotBlank(relationship.getRui())) {
if (!addedRUIs.contains(relationship.getRui())) {
this.importUtil.addStringAnnotation(r,
relationship.getRui(),
this.ptUMLSAttributes.getProperty("RUI")
.getUUID(),
State.ACTIVE);
addedRUIs.add(relationship.getRui());
this.satRelStatement.clearParameters();
this.satRelStatement.setString(1, relationship.getRui());
final ArrayList<RXNSAT> satData;
try (ResultSet nestedRels = this.satRelStatement.executeQuery()) {
satData = new ArrayList<>();
while (nestedRels.next()) {
satData.add(new RXNSAT(nestedRels));
}
}
processSAT(r, satData, null, relationship.getSab(), null);
}
}
if (StringUtils.isNotBlank(relationship.getRg())) {
this.importUtil.addStringAnnotation(r,
relationship.getRg(),
this.ptUMLSAttributes.getProperty("RG")
.getUUID(),
State.ACTIVE);
}
if (StringUtils.isNotBlank(relationship.getDir())) {
this.importUtil.addStringAnnotation(r,
relationship.getDir(),
this.ptUMLSAttributes.getProperty("DIR")
.getUUID(),
State.ACTIVE);
}
if (StringUtils.isNotBlank(relationship.getSuppress())) {
this.importUtil.addUUIDAnnotation(r,
this.suppress.get(relationship.getSuppress()),
this.ptUMLSAttributes.getProperty("SUPPRESS")
.getUUID());
}
if (StringUtils.isNotBlank(relationship.getCvf())) {
if (relationship.getCvf()
.equals("4096")) {
this.importUtil.addRefsetMembership(r, this.cpcRefsetConcept.getPrimordialUuid(), State.ACTIVE, null);
} else {
throw new RuntimeException("Unexpected value in RXNSAT cvf column '" + relationship.getCvf() + "'");
}
}
relCheckLoadedRel(relationship);
} else {
if (this.cuiToSCTID.containsKey(relationship.getSourceCUI())) {
// this is telling us there was a relationship from an SCT concept, to a RXNorm concept, but because we are
// not processing sct concept CUIs, we will never process this one in the forward direction.
// For now, don't put it in the skip list.
// Perhaps, in the future, we create a stub SCT concept, and create this association to the RxNorm concept
// but not now.
} else {
relCheckSkippedRel(relationship);
}
}
}
return parents;
}
/**
* Check relationships.
*/
private void checkRelationships() {
// if the inverse relationships all worked properly, skipped should be empty when loaded is subtracted from it.
for (final UUID uuid: this.loadedRels) {
this.skippedRels.remove(uuid);
}
if (this.skippedRels.size() > 0) {
ConsoleUtil.printErrorln("Relationship design error - " + this.skippedRels.size() +
" were skipped that should have been loaded");
} else {
ConsoleUtil.println("Yea! - no missing relationships!");
}
}
/**
* Clear target files.
*/
private void clearTargetFiles() {
new File(this.outputDirectory, "RxNormUUIDDebugMap.txt").delete();
new File(this.outputDirectory, "ConsoleOutput.txt").delete();
new File(this.outputDirectory, "RRF.jbin").delete();
}
/**
* Creates the CUI concept UUID.
*
* @param cui the cui
* @return the uuid
*/
private UUID createCUIConceptUUID(String cui) {
return ConverterUUID.createNamespaceUUIDFromString("CUI:" + cui, true);
}
/**
* Creates the sab query part.
*
* @param tablePrefix the table prefix
* @param includeSCT the include SCT
* @return the string
*/
private String createSabQueryPart(String tablePrefix, boolean includeSCT) {
final StringBuffer sb = new StringBuffer();
sb.append("(");
for (final String s: this.sabsToInclude) {
sb.append(tablePrefix + "SAB='" + s + "' OR ");
}
if (includeSCT) {
sb.append(tablePrefix + "SAB='" + this.sctSab + "' OR ");
}
sb.setLength(sb.length() - 4);
sb.append(")");
return sb.toString();
}
/**
* Finish.
*
* @throws IOException Signals that an I/O exception has occurred.
* @throws SQLException the SQL exception
*/
private void finish()
throws IOException, SQLException {
checkRelationships();
this.satRelStatement.close();
this.hasTTYType.close();
ConsoleUtil.println("Load Statistics");
for (final String s: this.importUtil.getLoadStats()
.getSummary()) {
ConsoleUtil.println(s);
}
// this could be removed from final release. Just added to help debug editor problems.
ConsoleUtil.println("Dumping UUID Debug File");
ConverterUUID.dump(this.outputDirectory, "RxNormUUID");
this.importUtil.shutdown();
ConsoleUtil.writeOutputToFile(new File(this.outputDirectory, "ConsoleOutput.txt").toPath());
}
/**
* If sabList is null or empty, no sab filtering is done.
*
* @throws Exception the exception
*/
private void init()
throws Exception {
clearTargetFiles();
final String fileNameDatePortion = loadDatabase();
final SimpleDateFormat sdf = new SimpleDateFormat("MMddyyyy");
final long defaultTime = sdf.parse(fileNameDatePortion)
.getTime();
this.abbreviationExpansions =
AbbreviationExpansion.load(getClass().getResourceAsStream("/RxNormAbbreviationsExpansions.txt"));
this.mapToIsa.put("isa", false);
this.mapToIsa.put("inverse_isa", false);
// not translating this one to isa for now
// mapToIsa.add("CHD");
this.mapToIsa.put("tradename_of", false);
this.mapToIsa.put("has_tradename", false);
// Cleanup the sabsToInclude list
final HashSet<String> temp = new HashSet<>();
if (this.sabsToInclude != null) {
for (final String s: this.sabsToInclude) {
temp.add(s.toUpperCase());
}
}
temp.add("RXNORM");
if (temp.contains(this.sctSab)) {
this.linkSnomedCT = true;
temp.remove(this.sctSab);
} else {
this.linkSnomedCT = false;
}
this.sabsToInclude = new ArrayList<>();
this.sabsToInclude.addAll(temp);
new File(this.inputFileLocation, "ibdf").listFiles((FileFilter) pathname -> {
if (RxNormMojo.this.linkSnomedCT &&
pathname.isFile() &&
pathname.getName().toLowerCase().endsWith(".ibdf")) {
return true;
}
return false;
});
this.importUtil = new IBDFCreationUtility(Optional.empty(),
Optional.of(MetaData.RXNORM_MODULES),
this.outputDirectory,
this.converterOutputArtifactId,
this.converterOutputArtifactVersion,
this.converterOutputArtifactClassifier,
false,
defaultTime);
this.metaDataRoot = ComponentReference.fromConcept(this.importUtil.createConcept("RxNorm Metadata" +
IBDFCreationUtility.METADATA_SEMANTIC_TAG,
true,
MetaData.SOLOR_CONTENT_METADATA.getPrimordialUuid()));
loadMetaData();
this.importUtil.loadTerminologyMetadataAttributes(this.metaDataRoot,
this.converterSourceArtifactVersion,
Optional.of(fileNameDatePortion),
this.converterOutputArtifactVersion,
Optional.ofNullable(this.converterOutputArtifactClassifier),
this.converterVersion);
ConsoleUtil.println("Metadata Statistics");
for (final String s: this.importUtil.getLoadStats()
.getSummary()) {
ConsoleUtil.println(s);
}
this.importUtil.clearLoadStats();
this.satRelStatement = this.db.getConnection()
.prepareStatement("select * from " + this.tablePrefix + "SAT where RXAUI" +
"= ? and STYPE='RUI' and " + createSabQueryPart("",
this.linkSnomedCT));
this.hasTTYType = this.db.getConnection()
.prepareStatement(
"select count (*) as count from RXNCONSO where rxcui=? and TTY=? and " +
createSabQueryPart("",
this.linkSnomedCT));
if (this.linkSnomedCT) {
prepareSCTMaps();
}
}
/**
* Returns the date portion of the file name - so from 'RxNorm_full_09022014.zip' it returns 09022014
*
* @return the string
* @throws Exception the exception
*/
private String loadDatabase()
throws Exception {
// Set up the DB for loading the temp data
String toReturn = null;
// Read the RRF file directly from the source zip file - need to find the zip first, to get the date out of the file name.
ZipFile zf = null;
for (final File f: this.inputFileLocation.listFiles()) {
if (f.getName().toLowerCase().startsWith("rxnorm_full_") && f.getName().toLowerCase().endsWith(".zip")) {
zf = new ZipFile(f);
toReturn = f.getName()
.substring("rxnorm_full_".length());
toReturn = toReturn.substring(0, toReturn.length() - 4);
break;
}
}
if (zf == null) {
throw new MojoExecutionException("Can't find source zip file");
}
this.db = new RRFDatabaseHandle();
final File dbFile = new File(this.outputDirectory, "rrfDB.h2.db");
final boolean createdNew = this.db.createOrOpenDatabase(new File(this.outputDirectory, "rrfDB"));
if (!createdNew) {
ConsoleUtil.println("Using existing database. To load from scratch, delete the file '" +
dbFile.getAbsolutePath() + ".*'");
} else {
// RxNorm doesn't give us the UMLS tables that define the table definitions, so I put them into an XML file.
final List<TableDefinition> tables =
this.db.loadTableDefinitionsFromXML(RxNormMojo.class.getResourceAsStream("/RxNormTableDefinitions.xml"));
for (final TableDefinition td: tables) {
final ZipEntry ze = zf.getEntry("rrf/" + td.getTableName() + ".RRF");
if (ze == null) {
throw new MojoExecutionException("Can't find the file 'rrf/" + td.getTableName() +
".RRF' in the zip file");
}
try (UMLSFileReader umlsReader =
new UMLSFileReader(new BufferedReader(new InputStreamReader(zf.getInputStream(ze),
"UTF-8")))) {
this.db.loadDataIntoTable(td, umlsReader, null);
}
}
zf.close();
try ( // Build some indexes to support the queries we will run
Statement s = this.db.getConnection().createStatement()) {
ConsoleUtil.println("Creating indexes");
ConsoleUtil.showProgress();
s.execute("CREATE INDEX conso_rxcui_index ON RXNCONSO (RXCUI)");
ConsoleUtil.showProgress();
s.execute("CREATE INDEX conso_rxaui_index ON RXNCONSO (RXAUI)");
ConsoleUtil.showProgress();
s.execute("CREATE INDEX sat_rxcui_aui_index ON RXNSAT (RXCUI, RXAUI)");
ConsoleUtil.showProgress();
s.execute("CREATE INDEX sat_aui_index ON RXNSAT (RXAUI)");
ConsoleUtil.showProgress();
s.execute("CREATE INDEX sty_rxcui_index ON RXNSTY (RXCUI)");
ConsoleUtil.showProgress();
s.execute("CREATE INDEX sty_tui_index ON RXNSTY (TUI)");
ConsoleUtil.showProgress();
s.execute("CREATE INDEX rel_rxcui2_index ON RXNREL (RXCUI2, RXAUI2)");
ConsoleUtil.showProgress();
s.execute("CREATE INDEX rel_rxaui2_index ON RXNREL (RXCUI1, RXAUI1)");
ConsoleUtil.showProgress();
s.execute("CREATE INDEX rel_rela_rel_index ON RXNREL (RELA, REL)"); // helps with rel metadata
ConsoleUtil.showProgress();
s.execute("CREATE INDEX rel_sab_index ON RXNREL (SAB)"); // helps with rel metadata
}
ConsoleUtil.println("DB Setup complete");
}
return toReturn;
}
/**
* Load meta data.
*
* @throws Exception the exception
*/
private void loadMetaData()
throws Exception {
this.ptRefsets = new PT_Refsets(rxNormName);
this.ptRefsets.addProperty(cpcRefsetConceptKey);
final PropertyType sourceMetadata = new PT_SAB_Metadata();
this.ptRelationshipMetadata = new PT_Relationship_Metadata();
this.ptUMLSAttributes = new PT_Annotations();
this.importUtil.loadMetaDataItems(Arrays.asList(this.ptRefsets,
sourceMetadata,
this.ptRelationshipMetadata,
this.ptUMLSAttributes),
this.metaDataRoot.getPrimordialUuid());
// Attributes from MRDoc
// dynamically add more attributes from *DOC
{
ConsoleUtil.println("Creating attribute types");
this.ptTermAttributes = new BPT_Annotations(rxNormName) {}
;
this.ptTermAttributes.indexByAltNames();
// extra logic at the end to keep NDC's from any sab when processing RXNorm
try (Statement s = this.db.getConnection().createStatement()) {
// extra logic at the end to keep NDC's from any sab when processing RXNorm
final ResultSet rs = s.executeQuery("SELECT VALUE, TYPE, EXPL from " + this.tablePrefix +
"DOC where DOCKEY = 'ATN' and VALUE in (select distinct ATN from " +
this.tablePrefix + "SAT" + " where " + createSabQueryPart("",
false) + " or ATN='NDC')");
while (rs.next()) {
final String abbreviation = rs.getString("VALUE");
final String type = rs.getString("TYPE");
final String expansion = rs.getString("EXPL");
if (!type.equals("expanded_form")) {
throw new RuntimeException("Unexpected type in the attribute data within DOC: '" + type + "'");
}
String altName = null;
String description = null;
if (expansion.length() > 30) {
description = expansion;
} else {
altName = expansion;
}
final AbbreviationExpansion ae = this.abbreviationExpansions.get(abbreviation);
if (ae == null) {
ConsoleUtil.printErrorln("No Abbreviation Expansion found for " + abbreviation);
this.ptTermAttributes.addProperty(abbreviation, altName, description);
} else {
this.ptTermAttributes.addProperty(ae.getExpansion(), ae.getAbbreviation(), ae.getDescription());
}
}
rs.close();
}
if (this.ptTermAttributes.getProperties()
.size() > 0) {
this.importUtil.loadMetaDataItems(this.ptTermAttributes, this.metaDataRoot.getPrimordialUuid());
}
}
// description types
{
ConsoleUtil.println("Creating description_ types");
this.ptDescriptions = new PT_Descriptions(rxNormName);
this.ptDescriptions.indexByAltNames();
final PreparedStatement ps;
try (Statement s = this.db.getConnection().createStatement()) {
ResultSet usedDescTypes;
usedDescTypes = s.executeQuery("select distinct TTY from RXNCONSO WHERE " + createSabQueryPart("", false));
ps = this.db.getConnection()
.prepareStatement("select TYPE, EXPL from " + this.tablePrefix +
"DOC where DOCKEY='TTY' and VALUE=?");
while (usedDescTypes.next()) {
final String tty = usedDescTypes.getString(1);
ps.setString(1, tty);
String expandedForm;
final HashSet<String> classes;
try (ResultSet descInfo = ps.executeQuery()) {
expandedForm = null;
classes = new HashSet<>();
while (descInfo.next()) {
final String type = descInfo.getString("TYPE");
final String expl = descInfo.getString("EXPL");
switch (type) {
case "expanded_form":
if (expandedForm != null) {
throw new RuntimeException("Expected name to be null!");
}
expandedForm = expl;
break;
case "tty_class":
classes.add(expl);
break;
default:
throw new RuntimeException("Unexpected type in DOC for '" + tty + "'");
}
}
}
ps.clearParameters();
Property p = null;
final AbbreviationExpansion ae = this.abbreviationExpansions.get(tty);
if (ae == null) {
ConsoleUtil.printErrorln("No Abbreviation Expansion found for " + tty);
p = makeDescriptionType(tty, expandedForm, null, classes);
} else {
p = makeDescriptionType(ae.getExpansion(), ae.getAbbreviation(), ae.getDescription(), classes);
}
this.ptDescriptions.addProperty(p);
for (final String tty_class: classes) {
this.importUtil.addStringAnnotation(ComponentReference.fromConcept(p.getUUID()),
tty_class,
this.ptUMLSAttributes.getProperty("tty_class")
.getUUID(),
State.ACTIVE);
}
}
usedDescTypes.close();
}
ps.close();
if (this.ptDescriptions.getProperties()
.size() > 0) {
this.importUtil.loadMetaDataItems(this.ptDescriptions, this.metaDataRoot.getPrimordialUuid());
}
}
loadRelationshipMetadata();
// STYPE values
this.sTypes = new HashMap<>();
{
ConsoleUtil.println("Creating STYPE types");
try (Statement s = this.db.getConnection().createStatement()) {
final ResultSet rs = s.executeQuery("SELECT DISTINCT VALUE, TYPE, EXPL FROM " + this.tablePrefix +
"DOC where DOCKEY like 'STYPE%'");
while (rs.next()) {
final String sType = rs.getString("VALUE");
final String type = rs.getString("TYPE");
final String name = rs.getString("EXPL");
if (!type.equals("expanded_form")) {
throw new RuntimeException("Unexpected type in the attribute data within DOC: '" + type + "'");
}
final ComponentReference c = ComponentReference.fromConcept(
this.importUtil.createConcept(
ConverterUUID.createNamespaceUUIDFromString(
this.ptUMLSAttributes.getProperty("STYPE")
.getUUID() + ":" + name),
name,
null,
null,
sType,
this.ptUMLSAttributes.getProperty("STYPE")
.getUUID(),
null));
this.sTypes.put(name, c.getPrimordialUuid());
this.sTypes.put(sType, c.getPrimordialUuid());
}
rs.close();
}
}
this.suppress = xDocLoaderHelper("SUPPRESS",
"Suppress",
false,
this.ptUMLSAttributes.getProperty("SUPPRESS")
.getUUID());
// Not yet loading co-occurrence data yet, so don't need these yet.
// xDocLoaderHelper("COA", "Attributes of co-occurrence", false);
// xDocLoaderHelper("COT", "Type of co-occurrence", true);
final HashMap<String, UUID> contextTypes = xDocLoaderHelper("CXTY",
"Context Type",
false,
sourceMetadata.getProperty("CXTY")
.getUUID());
// not yet loading mappings - so don't need this yet
// xDocLoaderHelper("FROMTYPE", "Mapping From Type", false);
// xDocLoaderHelper("TOTYPE", "Mapping To Type", false);
// MAPATN - not yet used in UMLS
// Handle the languages
// Not actually doing anythign with these at the moment, we just map to metadata languages.
{
try (Statement s = this.db.getConnection().createStatement()) {
final ResultSet rs = s.executeQuery("SELECT * from " + this.tablePrefix +
"DOC where DOCKEY = 'LAT' and VALUE in (select distinct LAT from " +
this.tablePrefix + "CONSO where " + createSabQueryPart("",
false) + ")");
while (rs.next()) {
final String abbreviation = rs.getString("VALUE");
final String type = rs.getString("TYPE");
// String expansion = rs.getString("EXPL");
if (!type.equals("expanded_form")) {
throw new RuntimeException("Unexpected type in the language data within DOC: '" + type + "'");
}
if (abbreviation.equals("ENG") || abbreviation.equals("SPA")) {
// use official ISAAC languages
if (abbreviation.equals("ENG") || abbreviation.equals("SPA")) {
// We can map these onto metadata types.
} else {
throw new RuntimeException("unsupported language");
}
}
}
rs.close();
}
}
// And Source Restriction Levels
{
ConsoleUtil.println("Creating Source Restriction Level types");
this.sourceRestrictionLevels = new HashMap<>();
try (PreparedStatement ps = this.db.getConnection().prepareStatement("SELECT VALUE, TYPE, EXPL from " +
this.tablePrefix + "DOC where DOCKEY=? ORDER BY VALUE")) {
ps.setString(1, "SRL");
final ResultSet rs = ps.executeQuery();
String value = null;
String description = null;
String uri = null;
// Two entries per SRL, read two rows, create an entry.
while (rs.next()) {
String type = rs.getString("TYPE");
String expl = rs.getString("EXPL");
switch (type) {
case "expanded_form":
description = expl;
break;
case "uri":
uri = expl;
break;
default:
throw new RuntimeException("oops");
}
if (value == null) {
value = rs.getString("VALUE");
} else {
if (!value.equals(rs.getString("VALUE"))) {
throw new RuntimeException("oops");
}
if ((description == null) || (uri == null)) {
throw new RuntimeException("oops");
}
final ComponentReference c = ComponentReference.fromConcept(
this.importUtil.createConcept(
ConverterUUID.createNamespaceUUIDFromString(
sourceMetadata.getProperty("SRL")
.getUUID() + ":" + value),
value,
null,
null,
description,
sourceMetadata.getProperty("SRL")
.getUUID(),
null));
this.sourceRestrictionLevels.put(value, c.getPrimordialUuid());
this.importUtil.addStringAnnotation(c,
uri,
this.ptUMLSAttributes.getProperty("URI")
.getUUID(),
State.ACTIVE);
type = null;
expl = null;
value = null;
}
}
rs.close();
}
}
// And Source vocabularies
final PreparedStatement getSABMetadata = this.db.getConnection()
.prepareStatement("Select * from " + this.tablePrefix +
"SAB where (VSAB = ? or (RSAB = ? and CURVER='Y' ))");
{
ConsoleUtil.println("Creating Source Vocabulary types");
this.ptSABs = new PropertyType("Source Vocabularies", true, DynamicSememeDataType.STRING) {}
;
this.ptSABs.indexByAltNames();
final HashSet<String> sabList = new HashSet<>();
sabList.addAll(this.sabsToInclude);
Statement s = this.db.getConnection()
.createStatement();
ResultSet rs = s.executeQuery("select distinct SAB from RXNSAT where ATN='NDC'");
while (rs.next()) {
sabList.add(rs.getString("SAB"));
}
rs.close();
s.close();
for (final String currentSab: sabList) {
s = this.db.getConnection()
.createStatement();
rs = s.executeQuery("SELECT SON from " + this.tablePrefix + "SAB WHERE (VSAB='" + currentSab +
"' or (RSAB='" + currentSab + "' and CURVER='Y'))");
if (rs.next()) {
final String son = rs.getString("SON");
final Property p = this.ptSABs.addProperty(son, currentSab, null);
final ComponentReference cr = ComponentReference.fromConcept(p.getUUID());
try {
// lookup the other columns for the row with this newly added RSAB terminology
getSABMetadata.setString(1,
(p.getSourcePropertyAltName() == null) ? p.getSourcePropertyNameFSN()
: p.getSourcePropertyAltName());
getSABMetadata.setString(2,
(p.getSourcePropertyAltName() == null) ? p.getSourcePropertyNameFSN()
: p.getSourcePropertyAltName());
try (ResultSet rs2 = getSABMetadata.executeQuery()) {
if (rs2.next()) {
for (final Property metadataProperty: sourceMetadata.getProperties()) {
final String columnName = (metadataProperty.getSourcePropertyAltName() == null)
? metadataProperty.getSourcePropertyNameFSN()
: metadataProperty.getSourcePropertyAltName();
final String columnValue = rs2.getString(columnName);
if (columnValue == null) {
continue;
}
switch (columnName) {
case "SRL":
this.importUtil.addUUIDAnnotation(cr,
this.sourceRestrictionLevels.get(columnValue),
metadataProperty.getUUID());
break;
case "CXTY":
this.importUtil.addUUIDAnnotation(cr,
contextTypes.get(columnValue),
sourceMetadata.getProperty("CXTY")
.getUUID());
break;
default:
this.importUtil.addStringAnnotation(cr,
columnValue,
metadataProperty.getUUID(),
State.ACTIVE);
break;
}
}
}
if (rs2.next()) {
throw new RuntimeException("Too many sabs. Perhaps you should be using versioned sabs!");
}
}
} catch (final SQLException e) {
throw new RuntimeException("Error loading *SAB", e);
}
} else {
throw new RuntimeException("Too few? SABs - perhaps you need to use versioned SABs.");
}
if (rs.next()) {
throw new RuntimeException("Too many SABs for '" + currentSab +
"' - perhaps you need to use versioned SABs.");
}
rs.close();
s.close();
}
this.importUtil.loadMetaDataItems(this.ptSABs, this.metaDataRoot.getPrimordialUuid());
getSABMetadata.close();
}
// And semantic types
{
ConsoleUtil.println("Creating semantic types");
try (Statement s = this.db.getConnection().createStatement()) {
final ResultSet rs = s.executeQuery("SELECT distinct TUI, STN, STY from " + this.tablePrefix + "STY");
while (rs.next()) {
final String tui = rs.getString("TUI");
final String stn = rs.getString("STN");
final String sty = rs.getString("STY");
final ComponentReference c = ComponentReference.fromConcept(
this.importUtil.createConcept(
ConverterUUID.createNamespaceUUIDFromString(
this.ptUMLSAttributes.getProperty("STY")
.getUUID() + ":" + sty),
sty,
null,
null,
null,
this.ptUMLSAttributes.getProperty("STY")
.getUUID(),
null));
this.semanticTypes.put(tui, c.getPrimordialUuid());
this.importUtil.addStringAnnotation(c,
tui,
this.ptUMLSAttributes.getProperty("TUI")
.getUUID(),
State.ACTIVE);
this.importUtil.addStringAnnotation(c,
stn,
this.ptUMLSAttributes.getProperty("STN")
.getUUID(),
State.ACTIVE);
}
rs.close();
}
}
}
/**
* Load relationship metadata.
*
* @throws Exception the exception
*/
private void loadRelationshipMetadata()
throws Exception {
ConsoleUtil.println("Creating relationship types");
// Both of these get added as extra attributes on the relationship definition
final HashMap<String, ArrayList<String>> snomedCTRelaMappings =
new HashMap<>(); // Maps something like 'has_specimen_source_morphology' to '118168003' (may be more than one target SCT code)
final HashMap<String, String> snomedCTRelMappings = new HashMap<>(); // Maps something like '118168003' to 'RO'
this.nameToRel = new HashMap<>();
Statement s = this.db.getConnection()
.createStatement();
// get the inverses of first, before the expanded forms
ResultSet rs = s.executeQuery("SELECT DOCKEY, VALUE, TYPE, EXPL FROM " + this.tablePrefix +
"DOC where DOCKEY ='REL' or DOCKEY = 'RELA' order by TYPE DESC ");
while (rs.next()) {
final String dockey = rs.getString("DOCKEY");
final String value = rs.getString("VALUE");
final String type = rs.getString("TYPE");
final String expl = rs.getString("EXPL");
if (value == null) {
continue; // don't need this one
}
switch (type) {
case "snomedct_rela_mapping":
ArrayList<String> targetSCTIDs = snomedCTRelaMappings.get(expl);
if (targetSCTIDs == null) {
targetSCTIDs = new ArrayList<>();
snomedCTRelaMappings.put(expl, targetSCTIDs);
}
targetSCTIDs.add(value);
break;
case "snomedct_rel_mapping":
snomedCTRelMappings.put(value, expl);
break;
default:
Relationship rel = this.nameToRel.get(value);
if (rel == null) {
if (type.endsWith("_inverse")) {
rel = this.nameToRel.get(expl);
if (rel == null) {
rel = new Relationship(dockey.equals("RELA"));
this.nameToRel.put(value, rel);
this.nameToRel.put(expl, rel);
} else {
throw new RuntimeException("shouldn't happen due to query order");
}
} else {
// only cases where there is no inverse
rel = new Relationship(dockey.equals("RELA"));
this.nameToRel.put(value, rel);
}
}
switch (type) {
case "expanded_form":
rel.addDescription(value, expl);
break;
case "rela_inverse":
case "rel_inverse":
rel.addRelInverse(value, expl);
break;
default:
throw new RuntimeException("Oops");
}
break;
}
}
rs.close();
s.close();
final HashSet<String> actuallyUsedRelsOrRelas = new HashSet<>();
for (final Entry<String, ArrayList<String>> x: snomedCTRelaMappings.entrySet()) {
if (!this.nameToRel.containsKey(x.getKey())) {
// metamorphosys doesn't seem to remove these when the sct rel types aren't included - just silently remove them
// unless it seems that they should map.
// may_be_a appears to be a bug in RxNorm 2013-12-02. silently ignore...
// TODO see if they fix it in the future, make this check version specific?
// seems to be getting worse... now it fails to remove 'has_life_circumstance' too in 2014AA, and a few others.
// Changing to a warning.
ConsoleUtil.printErrorln("Warning - The 'snomedct_rela_mapping' '" + x.getKey() +
"' does not have a corresponding REL entry! Skipping");
// if (!x.getKey().equals("may_be_a") && !x.getKey().equals("has_life_circumstance"))
// {
// throw new RuntimeException("ERROR - No rel for " + x.getKey() + ".");
// }
for (final String sctId: x.getValue()) {
snomedCTRelMappings.remove(sctId);
}
} else {
for (final String sctid: x.getValue()) {
this.nameToRel.get(x.getKey())
.addSnomedCode(x.getKey(), sctid);
final String relType = snomedCTRelMappings.remove(sctid);
if (relType != null) {
this.nameToRel.get(x.getKey())
.addRelType(x.getKey(), relType);
// Shouldn't need this, but there are some cases where the metadata is inconsistent - with how it is actually used.
actuallyUsedRelsOrRelas.add(relType);
}
}
}
}
if (snomedCTRelMappings.size() > 0) {
for (final Entry<String, String> x: snomedCTRelMappings.entrySet()) {
ConsoleUtil.printErrorln(x.getKey() + ":" + x.getValue());
}
throw new RuntimeException("oops - still have (things listed above)");
}
this.ptRelationships = new BPT_Relations(rxNormName) {}
;
this.ptRelationships.indexByAltNames();
this.ptAssociations = new BPT_Associations() {}
;
this.ptAssociations.indexByAltNames();
s = this.db.getConnection()
.createStatement();
rs = s.executeQuery("select distinct REL, RELA from " + this.tablePrefix + "REL where " + createSabQueryPart("",
this.linkSnomedCT));
while (rs.next()) {
actuallyUsedRelsOrRelas.add(rs.getString("REL"));
if (rs.getString("RELA") != null) {
actuallyUsedRelsOrRelas.add(rs.getString("RELA"));
}
}
rs.close();
s.close();
final HashSet<Relationship> uniqueRels = new HashSet<>(this.nameToRel.values());
// Sort the generic relationships first, these are needed when processing primary
final ArrayList<Relationship> sortedRels = new ArrayList<>(uniqueRels);
Collections.sort(sortedRels,
(o1, o2) -> {
if (o1.getIsRela() &&!o2.getIsRela()) {
return 1;
}
if (o2.getIsRela() &&!o1.getIsRela()) {
return -1;
}
return 0;
});
for (final Relationship r: sortedRels) {
r.setSwap(this.db.getConnection(), this.tablePrefix);
if (!actuallyUsedRelsOrRelas.contains(r.getFSNName()) &&
!actuallyUsedRelsOrRelas.contains(r.getInverseFSNName())) {
continue;
}
Property p = null;
final Boolean relTypeMap = this.mapToIsa.get(r.getFSNName());
if (relTypeMap != null) // true or false, make it a rel
{
p = new Property(((r.getAltName() == null) ? r.getFSNName()
: r.getAltName()), ((r.getAltName() == null) ? null
: r.getFSNName()), r.getDescription(), MetaData.IS_A.getPrimordialUuid()); // map to isA
this.ptRelationships.addProperty(
p); // conveniently, the only thing we will treat as relationships are things mapped to isa.
}
if ((relTypeMap == null) || (relTypeMap == false)) // don't make it an association if set to true
{
p = new PropertyAssociation(null, ((r.getAltName() == null) ? r.getFSNName()
: r.getAltName()), ((r.getAltName() == null) ? null
: r.getFSNName()), ((r.getInverseAltName() == null) ? r.getInverseFSNName()
: r.getInverseAltName()), r.getDescription(), false);
this.ptAssociations.addProperty(p);
}
final ComponentReference cr = ComponentReference.fromConcept(p.getUUID());
// associations already handle inverse names
if (!(p instanceof PropertyAssociation) && (r.getInverseFSNName() != null)) {
this.importUtil.addDescription(cr,
((r.getInverseAltName() == null) ? r.getInverseFSNName()
: r.getInverseAltName()),
DescriptionType.FSN,
false,
this.ptDescriptions.getProperty("Inverse FSN")
.getUUID(),
State.ACTIVE);
}
if (r.getAltName() != null) {
// Need to create this UUID to be different than forward name, in case forward and reverse are identical (like 'RO')
final UUID descUUID = ConverterUUID.createNamespaceUUIDFromStrings(cr.getPrimordialUuid()
.toString(),
r.getInverseFSNName(),
DescriptionType.SYNONYM.name(),
"false",
"inverse");
// Yes, this looks funny, no its not a copy/paste error. We swap the FSN and alt names for... it a long story. 42.
this.importUtil.addDescription(cr,
descUUID,
r.getInverseFSNName(),
DescriptionType.SYNONYM,
false,
null,
null,
null,
null,
this.ptDescriptions.getProperty("Inverse Synonym")
.getUUID(),
State.ACTIVE,
null);
}
if (r.getInverseDescription() != null) {
this.importUtil.addDescription(cr,
r.getInverseDescription(),
DescriptionType.DEFINITION,
true,
this.ptDescriptions.getProperty("Inverse Description")
.getUUID(),
State.ACTIVE);
}
if (r.getRelType() != null) {
final Relationship generalRel = this.nameToRel.get(r.getRelType());
this.importUtil.addUUIDAnnotation(cr,
(this.mapToIsa.containsKey(generalRel.getFSNName())
? this.ptRelationships.getProperty(generalRel.getFSNName())
: this.ptAssociations.getProperty(generalRel.getFSNName())).getUUID(),
this.ptRelationshipMetadata.getProperty("General Rel Type")
.getUUID());
}
if (r.getInverseRelType() != null) {
final Relationship generalRel = this.nameToRel.get(r.getInverseRelType());
this.importUtil.addUUIDAnnotation(cr,
(this.mapToIsa.containsKey(generalRel.getFSNName())
? this.ptRelationships.getProperty(generalRel.getFSNName())
: this.ptAssociations.getProperty(generalRel.getFSNName())).getUUID(),
this.ptRelationshipMetadata.getProperty("Inverse General Rel Type")
.getUUID());
}
for (final String sctCode: r.getRelSnomedCode()) {
this.importUtil.addUUIDAnnotation(cr,
UuidT3Generator.fromSNOMED(sctCode),
this.ptRelationshipMetadata.getProperty("Snomed Code")
.getUUID());
}
for (final String sctCode: r.getInverseRelSnomedCode()) {
this.importUtil.addUUIDAnnotation(cr,
UuidT3Generator.fromSNOMED(sctCode),
this.ptRelationshipMetadata.getProperty("Inverse Snomed Code")
.getUUID());
}
}
if (this.ptRelationships.getProperties()
.size() > 0) {
this.importUtil.loadMetaDataItems(this.ptRelationships, this.metaDataRoot.getPrimordialUuid());
}
if (this.ptAssociations.getProperties()
.size() > 0) {
this.importUtil.loadMetaDataItems(this.ptAssociations, this.metaDataRoot.getPrimordialUuid());
}
}
/**
* Make description type.
*
* @param fsnName the fsn name
* @param altName the alt name
* @param description the description
* @param tty_classes the tty classes
* @return the property
*/
private Property makeDescriptionType(String fsnName,
String altName,
String description,
final Set<String> tty_classes) {
// The current possible classes are:
// preferred
// obsolete
// entry_term
// hierarchical
// synonym
// attribute
// abbreviation
// expanded
// other
int descriptionTypeRanking;
// Note - ValuePropertyPairWithSAB overrides the sorting based on these values to kick RXNORM sabs to the top, where
// they will get used as FSN.
if (fsnName.equals("FN") && tty_classes.contains("preferred")) {
descriptionTypeRanking = BPT_Descriptions.FSN;
} else if (fsnName.equals("FN")) {
descriptionTypeRanking = BPT_Descriptions.FSN + 1;
} // preferred gets applied with others as well, in some cases. Don't want 'preferred' 'obsolete' at the top.
// Just preferred, and we make it the top synonym.
else if (tty_classes.contains("preferred") && (tty_classes.size() == 1)) {
// these sub-rankings are somewhat arbitrary at the moment, and in general, unused. There is an error check up above which
// will fail the conversion if it tries to rely on these sub-rankings to find a preferred term
int preferredSubRank;
switch (altName) {
case "IN":
preferredSubRank = 1;
break;
case "MIN":
preferredSubRank = 2;
break;
case "PIN":
preferredSubRank = 3;
break;
case "SCD":
preferredSubRank = 4;
break;
case "BN":
preferredSubRank = 5;
break;
case "SBD":
preferredSubRank = 6;
break;
case "DF":
preferredSubRank = 7;
break;
case "BPCK":
preferredSubRank = 8;
break;
case "GPCK":
preferredSubRank = 10;
break;
case "DFG":
preferredSubRank = 11;
break;
case "PSN":
preferredSubRank = 12;
break;
case "SBDC":
preferredSubRank = 13;
break;
case "SCDC":
preferredSubRank = 14;
break;
case "SBDF":
preferredSubRank = 15;
break;
case "SCDF":
preferredSubRank = 16;
break;
case "SBDG":
preferredSubRank = 17;
break;
case "SCDG":
preferredSubRank = 18;
break;
default:
preferredSubRank = 20;
ConsoleUtil.printErrorln("Unranked preferred TTY type! " + fsnName + " " + altName);
break;
}
descriptionTypeRanking = BPT_Descriptions.SYNONYM + preferredSubRank;
} else if (tty_classes.contains("entry_term")) {
descriptionTypeRanking = BPT_Descriptions.SYNONYM + 21;
} else if (tty_classes.contains("synonym")) {
descriptionTypeRanking = BPT_Descriptions.SYNONYM + 22;
} else if (tty_classes.contains("expanded")) {
descriptionTypeRanking = BPT_Descriptions.SYNONYM + 23;
} else if (tty_classes.contains("Prescribable Name")) {
descriptionTypeRanking = BPT_Descriptions.SYNONYM + 24;
} else if (tty_classes.contains("abbreviation")) {
descriptionTypeRanking = BPT_Descriptions.SYNONYM + 25;
} else if (tty_classes.contains("attribute")) {
descriptionTypeRanking = BPT_Descriptions.SYNONYM + 26;
} else if (tty_classes.contains("hierarchical")) {
descriptionTypeRanking = BPT_Descriptions.SYNONYM + 27;
} else if (tty_classes.contains("other")) {
descriptionTypeRanking = BPT_Descriptions.SYNONYM + 28;
} else if (tty_classes.contains("obsolete")) {
descriptionTypeRanking = BPT_Descriptions.SYNONYM + 29;
} else {
throw new RuntimeException("Unexpected class type " + Arrays.toString(tty_classes.toArray()));
}
return new Property(null, fsnName, altName, description, false, descriptionTypeRanking, null);
}
/**
* Prepare SCT maps.
*
* @throws SQLException the SQL exception
*/
private void prepareSCTMaps()
throws SQLException {
Get.sememeService()
.getSememeSequencesFromAssemblage(MetaData.SCTID.getConceptSequence())
.stream()
.forEach(sememe -> {
@SuppressWarnings({ "unchecked", "rawtypes" })
final Optional<LatestVersion<StringSememe<?>>> lv = ((SememeChronology) Get.sememeService()
.getSememe(
sememe)).getLatestVersion(
StringSememe.class,
StampCoordinates.getDevelopmentLatest());
final StringSememe<?> ss = lv.get()
.value();
final Long sctId = Long.parseLong(ss.getString());
final UUID conceptUUID = Get.identifierService()
.getUuidPrimordialForNid(ss.getReferencedComponentNid())
.get();
this.sctIdToUUID.put(sctId, conceptUUID);
});
ConsoleUtil.println("Read SCTID -> UUID mappings for " + this.sctIdToUUID.size() + " items");
try (ResultSet rs = this.db.getConnection().createStatement().executeQuery(
"SELECT DISTINCT RXCUI, CODE from RXNCONSO where SAB='" + this.sctSab + "'")) {
while (rs.next()) {
final String cui = rs.getString("RXCUI");
final long sctid = Long.parseLong(rs.getString("CODE"));
if (this.sctIdToUUID.containsKey(sctid)) {
this.cuiToSCTID.put(cui, sctid);
}
}
}
ConsoleUtil.println("Read CUI -> SCTID mappings for " + this.cuiToSCTID.size() + " items");
}
/**
* Process CUI rows.
*
* @param conceptData the concept data
* @throws IOException Signals that an I/O exception has occurred.
* @throws SQLException the SQL exception
* @throws PropertyVetoException the property veto exception
*/
private void processCUIRows(ArrayList<RXNCONSO> conceptData)
throws IOException,
SQLException,
PropertyVetoException {
final String rxCui = conceptData.get(0).rxcui;
final HashSet<String> uniqueTTYs = new HashSet<>();
final HashSet<String> uniqueSABs = new HashSet<>();
// ensure all the same CUI, gather the TTYs involved
for (final RXNCONSO row: conceptData) {
uniqueTTYs.add(row.tty);
uniqueSABs.add(row.sab);
if (!row.rxcui.equals(rxCui)) {
throw new RuntimeException("Oops");
}
}
ComponentReference cuiConcept;
if ((uniqueSABs.size() == 1) && uniqueSABs.iterator().next().equals(this.sctSab)) {
// This is a SCT only concept - we don't want to create it. But we might need to put some relationships or associations here.
final String sctId = conceptData.get(0).code;
if (sctId == null) {
throw new RuntimeException("Unexpected");
}
cuiConcept = ComponentReference.fromConcept(this.sctIdToUUID.get(sctId));
// Add the RxCUI UUID
this.importUtil.addUUID(cuiConcept.getPrimordialUuid(), createCUIConceptUUID(rxCui));
// TODO need to look at what else I should be grabbing - the RXCUI for example should be attached. What else?
} else {
// just creating the reference here, with the UUID - because we don't know if it should be active or inactive yet.
// create the real concept later.
cuiConcept = ComponentReference.fromConcept(createCUIConceptUUID(rxCui));
long conceptTime = Integer.MAX_VALUE;
// Activate the concept if any description is active
State conceptState = State.INACTIVE;
this.importUtil.addStringAnnotation(cuiConcept,
rxCui,
this.ptUMLSAttributes.getProperty("RXCUI")
.getUUID(),
State.ACTIVE);
final ArrayList<ValuePropertyPairWithSAB> cuiDescriptions = new ArrayList<>();
final HashSet<String> sabs = new HashSet<>();
for (final RXNCONSO atom: conceptData) {
if (atom.sab.equals(this.sctSab)) {
continue;
}
// Add attributes from SAT table
this.descSat.clearParameters();
this.descSat.setString(1, rxCui);
this.descSat.setString(2, atom.rxaui);
final ArrayList<RXNSAT> satData;
boolean disableDescription;
Long descriptionTime;
try (ResultSet rs = this.descSat.executeQuery()) {
satData = new ArrayList<>();
disableDescription = false;
descriptionTime = null;
while (rs.next()) {
final RXNSAT current = new RXNSAT(rs);
satData.add(current);
if ("RXN_OBSOLETED".equals(current.atn)) {
disableDescription = true;
}
if ("RXN_ACTIVATED".equals(current.atn)) {
try {
final long time = this.dateParse.parse(current.atv)
.getTime();
descriptionTime = time;
if (time < conceptTime) {
conceptTime = time;
}
} catch (final ParseException e) {
throw new RuntimeException("Can't parse date?");
}
}
}
}
final ValuePropertyPairWithSAB desc = new ValuePropertyPairWithSAB(atom.str,
this.ptDescriptions.getProperty(
atom.tty),
atom.sab,
satData);
if (disableDescription) {
desc.setDisabled(true);
} else {
// if any description is active, concept is still active
conceptState = State.ACTIVE;
}
if (descriptionTime != null) {
desc.setTime(descriptionTime);
}
desc.setUUID(ConverterUUID.createNamespaceUUIDFromStrings(cuiConcept.getPrimordialUuid()
.toString(),
atom.rxaui));
// used for sorting description to figure out what to use for FSN
cuiDescriptions.add(desc);
desc.addStringAttribute(this.ptUMLSAttributes.getProperty("RXAUI")
.getUUID(), atom.rxaui);
desc.addUUIDAttribute(this.ptUMLSAttributes.getProperty("SAB")
.getUUID(),
this.ptSABs.getProperty(atom.sab)
.getUUID());
if (StringUtils.isNotBlank(atom.code) &&!atom.code.equals("NOCODE")) {
desc.addStringAttribute(this.ptUMLSAttributes.getProperty("CODE")
.getUUID(), atom.code);
}
if (StringUtils.isNotBlank(atom.saui)) {
desc.addStringAttribute(this.ptUMLSAttributes.getProperty("SAUI")
.getUUID(), atom.saui);
}
if (StringUtils.isNotBlank(atom.scui)) {
desc.addStringAttribute(this.ptUMLSAttributes.getProperty("SCUI")
.getUUID(), atom.scui);
}
if (StringUtils.isNotBlank(atom.suppress)) {
desc.addUUIDAttribute(this.ptUMLSAttributes.getProperty("SUPPRESS")
.getUUID(),
this.suppress.get(atom.suppress));
}
if (StringUtils.isNotBlank(atom.cvf)) {
if (atom.cvf.equals("4096")) {
desc.addRefsetMembership(this.cpcRefsetConcept.getPrimordialUuid());
} else {
throw new RuntimeException("Unexpected value in RXNCONSO cvf column '" + atom.cvf + "'");
}
}
if (!atom.lat.equals("ENG")) {
ConsoleUtil.printErrorln("Non-english lang settings not handled yet!");
}
// TODO - at this point, sometime in the future, we make make attributes out of the relationships that occur between the AUIs
// and store them on the descriptions, since OTF doesn't allow relationships between descriptions
// TODO am I supposed to be using sabs?
sabs.add(atom.sab);
}
// sanity check on descriptions - make sure we only have one that is of type synonym with the preferred flag
final ArrayList<String> items = new ArrayList<>();
for (final ValuePropertyPair vpp: cuiDescriptions) {
// Numbers come from the rankings down below in makeDescriptionType(...)
if ((vpp.getProperty().getPropertySubType() >= BPT_Descriptions.SYNONYM) &&
(vpp.getProperty().getPropertySubType() <= (BPT_Descriptions.SYNONYM + 20))) {
items.add(vpp.getProperty()
.getSourcePropertyNameFSN() + " " + vpp.getProperty().getPropertySubType());
}
}
final HashSet<String> ranksLookedAt = new HashSet<>();
ranksLookedAt.add("204");
ranksLookedAt.add("206");
ranksLookedAt.add("210");
ranksLookedAt.add("208");
ranksLookedAt.add("212");
boolean oneNotInList = false;
if (items.size() > 1) {
for (final String s: items) {
if (!ranksLookedAt.contains(s.substring(s.length() - 3, s.length()))) {
oneNotInList = true;
break;
}
}
}
if (oneNotInList) {
ConsoleUtil.printErrorln(
"Need to rank multiple synonym types that are each marked preferred, determine if ranking is appropriate!");
for (final String s: items) {
ConsoleUtil.printErrorln(s);
}
}
final List<SememeChronology<DescriptionSememe<?>>> addedDescriptions =
this.importUtil.addDescriptions(cuiConcept,
cuiDescriptions);
if (addedDescriptions.size() != cuiDescriptions.size()) {
throw new RuntimeException("oops");
}
final HashSet<String> uniqueUMLSCUI = new HashSet<>();
for (int i = 0; i < cuiDescriptions.size(); i++) {
final SememeChronology<DescriptionSememe<?>> desc = addedDescriptions.get(i);
final ValuePropertyPairWithSAB descPP = cuiDescriptions.get(i);
final BiFunction<String, String, Boolean> functions = (atn, atv) -> {
// Pull these up to the concept.
if ("UMLSCUI".equals(atn)) {
uniqueUMLSCUI.add(atv);
return true;
}
return false;
};
// TODO should I be passing in item code here?
processSAT(ComponentReference.fromChronology(desc), descPP.getSatData(), null, descPP.getSab(), functions);
}
// pulling up the UMLS CUIs.
// uniqueUMLSCUI is populated during processSAT
for (final String umlsCui: uniqueUMLSCUI) {
final UUID itemUUID = ConverterUUID.createNamespaceUUIDFromString("UMLSCUI" + umlsCui);
this.importUtil.addStringAnnotation(cuiConcept,
itemUUID,
umlsCui,
this.ptTermAttributes.getProperty("UMLSCUI")
.getUUID(),
State.ACTIVE);
}
ValuePropertyPairWithAttributes.processAttributes(this.importUtil, cuiDescriptions, addedDescriptions);
// there are no attributes in rxnorm without an AUI.
// try
// {
this.importUtil.addRefsetMembership(cuiConcept,
this.allCUIRefsetConcept.getPrimordialUuid(),
State.ACTIVE,
null);
// }
// catch (RuntimeException e)
// {
// if (e.toString().contains("duplicate UUID"))
// {
// //ok - this can happen due to multiple merges onto an existing SCT concept
// }
// else
// {
// throw e;
// }
// }
// add semantic types
this.semanticTypeStatement.clearParameters();
this.semanticTypeStatement.setString(1, rxCui);
final ResultSet rs = this.semanticTypeStatement.executeQuery();
processSemanticTypes(cuiConcept, rs);
if (conceptTime < 0) {
throw new RuntimeException("oops");
}
this.importUtil.createConcept(cuiConcept.getPrimordialUuid(), conceptTime, conceptState, null);
}
final HashSet<UUID> parents = new HashSet<>();
this.cuiRelStatementForward.clearParameters();
this.cuiRelStatementForward.setString(1, rxCui);
parents.addAll(addRelationships(cuiConcept,
REL.read(null,
this.cuiRelStatementForward.executeQuery(),
true,
this.allowedCUIsForSABs,
this.skippedRelForNotMatchingCUIFilter,
true,
(string -> reverseRel(string)))));
this.cuiRelStatementBackward.clearParameters();
this.cuiRelStatementBackward.setString(1, rxCui);
parents.addAll(addRelationships(cuiConcept,
REL.read(null,
this.cuiRelStatementBackward.executeQuery(),
false,
this.allowedCUIsForSABs,
this.skippedRelForNotMatchingCUIFilter,
true,
(string -> reverseRel(string)))));
// Have to add multiple parents at once, no place to keep all the other details. Load those as associations for now.
if (parents.size() > 0) {
ComponentReference.fromChronology(this.importUtil.addParent(cuiConcept,
null,
parents.toArray(new UUID[parents.size()]),
null,
null));
}
}
/**
* Process SAT.
*
* @param itemToAnnotate the item to annotate
* @param satRows the sat rows
* @param itemCode the item code
* @param itemSab the item sab
* @param skipCheck the skip check
* @throws SQLException the SQL exception
* @throws PropertyVetoException the property veto exception
*/
private void processSAT(ComponentReference itemToAnnotate,
List<RXNSAT> satRows,
String itemCode,
String itemSab,
BiFunction<String, String, Boolean> skipCheck)
throws SQLException,
PropertyVetoException {
for (final RXNSAT rxnsat: satRows) {
if (skipCheck != null) {
if (skipCheck.apply(rxnsat.atn, rxnsat.atv)) {
continue;
}
}
// for some reason, ATUI isn't always provided - don't know why. must gen differently in those cases...
UUID stringAttrUUID;
final UUID refsetUUID = this.ptTermAttributes.getProperty(rxnsat.atn)
.getUUID();
if (rxnsat.atui != null) {
stringAttrUUID = ConverterUUID.createNamespaceUUIDFromString("ATUI" + rxnsat.atui);
} else {
// need to put the aui in here, to keep it unique, as each AUI frequently specs the same CUI
stringAttrUUID = ConverterUUID.createNamespaceUUIDFromStrings(itemToAnnotate.getPrimordialUuid()
.toString(),
rxnsat.rxaui,
rxnsat.atv,
refsetUUID.toString());
}
// You would expect that ptTermAttributes_.get() would be looking up sab, rather than having RxNorm hardcoded... but this is an oddity of
// a hack we are doing within the RxNorm load.
final ComponentReference attribute =
ComponentReference.fromChronology(this.importUtil.addStringAnnotation(itemToAnnotate,
stringAttrUUID,
rxnsat.atv,
refsetUUID,
State.ACTIVE),
() -> "Attribute");
if (StringUtils.isNotBlank(rxnsat.atui)) {
this.importUtil.addStringAnnotation(attribute,
rxnsat.atui,
this.ptUMLSAttributes.getProperty("ATUI")
.getUUID(),
null);
}
if (StringUtils.isNotBlank(rxnsat.stype)) {
this.importUtil.addUUIDAnnotation(attribute,
this.sTypes.get(rxnsat.stype),
this.ptUMLSAttributes.getProperty("STYPE")
.getUUID());
}
if (StringUtils.isNotBlank(rxnsat.code) && StringUtils.isNotBlank(itemCode) &&!rxnsat.code.equals(itemCode)) {
throw new RuntimeException("oops");
// if ()
// {
// eConcepts_.addStringAnnotation(attribute, code, ptUMLSAttributes_.getProperty("CODE").getUUID(), State.ACTIVE);
// }
}
if (StringUtils.isNotBlank(rxnsat.satui)) {
this.importUtil.addStringAnnotation(attribute,
rxnsat.satui,
this.ptUMLSAttributes.getProperty("SATUI")
.getUUID(),
State.ACTIVE);
}
// only load the sab if it is different than the sab of the item we are putting this attribute on
if (StringUtils.isNotBlank(rxnsat.sab) &&!rxnsat.sab.equals(itemSab)) {
throw new RuntimeException("Oops");
// eConcepts_.addUuidAnnotation(attribute, ptSABs_.getProperty(sab).getUUID(), ptUMLSAttributes_.getProperty("SAB").getUUID());
}
if (StringUtils.isNotBlank(rxnsat.suppress)) {
this.importUtil.addUUIDAnnotation(attribute,
this.suppress.get(rxnsat.suppress),
this.ptUMLSAttributes.getProperty("SUPPRESS")
.getUUID());
}
if (StringUtils.isNotBlank(rxnsat.cvf)) {
if (rxnsat.cvf.equals("4096")) {
this.importUtil.addRefsetMembership(attribute,
this.cpcRefsetConcept.getPrimordialUuid(),
State.ACTIVE,
null);
} else {
throw new RuntimeException("Unexpected value in RXNSAT cvf column '" + rxnsat.cvf + "'");
}
}
}
}
/**
* Process semantic types.
*
* @param concept the concept
* @param rs the rs
* @throws SQLException the SQL exception
*/
private void processSemanticTypes(ComponentReference concept, ResultSet rs)
throws SQLException {
while (rs.next()) {
// try
// {
final ComponentReference annotation =
ComponentReference.fromChronology(this.importUtil.addUUIDAnnotation(concept,
this.semanticTypes.get(
rs.getString("TUI")),
this.ptUMLSAttributes.getProperty("STY")
.getUUID()),
() -> "Sememe Member");
if (rs.getString("ATUI") != null) {
this.importUtil.addStringAnnotation(annotation,
rs.getString("ATUI"),
this.ptUMLSAttributes.getProperty("ATUI")
.getUUID(),
State.ACTIVE);
}
if (rs.getObject("CVF") != null) // might be an int or a string
{
this.importUtil.addStringAnnotation(annotation,
rs.getString("CVF"),
this.ptUMLSAttributes.getProperty("CVF")
.getUUID(),
State.ACTIVE);
}
// }
// catch (RuntimeException e)
// {
// //ok if dupe - this can happen due to multiple merges onto an existing SCT concept
// if (!e.toString().contains("duplicate UUID"))
// {
// throw e;
// }
// }
}
rs.close();
}
/**
* Rel check is rel loaded.
*
* @param rel the rel
* @return true, if successful
*/
private boolean relCheckIsRelLoaded(REL rel) {
return this.loadedRels.contains(rel.getRelHash());
}
/**
* Rel check loaded rel.
*
* @param rel the rel
*/
private void relCheckLoadedRel(REL rel) {
this.loadedRels.add(rel.getRelHash());
this.skippedRels.remove(rel.getRelHash());
}
/**
* Call this when a rel wasn't added because the rel was listed with the inverse name, rather than the primary name.
*
* @param rel the rel
*/
private void relCheckSkippedRel(REL rel) {
this.skippedRels.add(rel.getInverseRelHash(string -> this.nameToRel.get(string)));
}
/**
* Reverse rel.
*
* @param eitherRelType the either rel type
* @return the string
*/
private String reverseRel(String eitherRelType) {
if (eitherRelType == null) {
return null;
}
final Relationship r = this.nameToRel.get(eitherRelType);
if (r.getFSNName()
.equals(eitherRelType)) {
return r.getInverseFSNName();
} else if (r.getInverseFSNName()
.equals(eitherRelType)) {
return r.getFSNName();
} else {
throw new RuntimeException("gak");
}
}
/**
* X doc loader helper.
*
* @param dockey the dockey
* @param niceName the nice name
* @param loadAsDefinition the load as definition
* @param parent the parent
* @return the hash map
* @throws Exception the exception
*/
/*
* Note - may return null, if there were no instances of the requested data
*/
private HashMap<String, UUID> xDocLoaderHelper(String dockey,
String niceName,
boolean loadAsDefinition,
UUID parent)
throws Exception {
final HashMap<String, UUID> result = new HashMap<>();
ConsoleUtil.println("Creating '" + niceName + "' types");
{
try (Statement s = this.db.getConnection().createStatement()) {
final ResultSet rs = s.executeQuery("SELECT VALUE, TYPE, EXPL FROM " + this.tablePrefix +
"DOC where DOCKEY='" + dockey + "'");
while (rs.next()) {
final String value = rs.getString("VALUE");
final String type = rs.getString("TYPE");
final String name = rs.getString("EXPL");
if (value == null) {
// there is a null entry, don't care about it.
continue;
}
if (!type.equals("expanded_form")) {
throw new RuntimeException("Unexpected type in the attribute data within DOC: '" + type + "'");
}
final UUID created = this.importUtil.createConcept(ConverterUUID.createNamespaceUUIDFromString(parent +
":" + (loadAsDefinition ? value
: name)), (loadAsDefinition ? value
: name), null, (loadAsDefinition ? null
: value), (loadAsDefinition ? name
: null), parent, null)
.getPrimordialUuid();
result.put((loadAsDefinition ? value
: name), created);
if (!loadAsDefinition) {
result.put(value, created);
}
}
rs.close();
}
}
if (result.isEmpty()) {
// This can happen, depending on what is included during the metamorphosys run
ConsoleUtil.println("No entries found for '" + niceName + "' - skipping");
return null;
}
return result;
}
//~--- get methods ---------------------------------------------------------
@Override
protected ConverterUUID.NAMESPACE getNamespace() {
return ConverterUUID.NAMESPACE.RXNORM;
}
/**
* Checks if rel primary.
*
* @param relName the rel name
* @param relaName the rela name
* @return true, if rel primary
*/
private boolean isRelPrimary(String relName, String relaName) {
if (relaName != null) {
return this.nameToRel.get(relaName)
.getFSNName()
.equals(relaName);
} else {
return this.nameToRel.get(relName)
.getFSNName()
.equals(relName);
}
}
}