org.immregistries.codebase.client.CodeMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of codebase-client Show documentation
Show all versions of codebase-client Show documentation
Reads the Codebase XML and provides helper classes to use it.
The newest version!
package org.immregistries.codebase.client;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.immregistries.codebase.client.generated.Code;
import org.immregistries.codebase.client.generated.Codebase;
import org.immregistries.codebase.client.generated.Codeset;
import org.immregistries.codebase.client.generated.LinkTo;
import org.immregistries.codebase.client.generated.Reference;
import org.immregistries.codebase.client.generated.UseDate;
import org.immregistries.codebase.client.reference.CodeStatusValue;
import org.immregistries.codebase.client.reference.CodesetType;
import org.immregistries.codebase.client.reference.Ops;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CodeMap {
private static final Logger logger = LoggerFactory.getLogger(CodeMap.class);
private Map> codeBaseMap;
private Map> productMap = new HashMap>();
private static final String MAP_DELIMITER = "::";
private final DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMdd");
public List getProductsFor(String vaccineCvx, String vaccineMvx) {
if (StringUtils.isBlank(vaccineCvx) || StringUtils.isBlank(vaccineMvx)) {
return new ArrayList();
}
List productCodes = productMap.get(vaccineCvx + MAP_DELIMITER + vaccineMvx);
if (productCodes == null) {
return new ArrayList();
}
return productCodes;
}
public String getRelatedValue(Code codeIn, CodesetType desiredType) {
String relatedValue = "";
if (codeIn == null) {
return relatedValue;
}
if (desiredType == null) {
return relatedValue;
}
Reference r = codeIn.getReference();
if (r == null) {
return relatedValue;
}
List linkList = r.getLinkTo();
for (LinkTo link : linkList) {
if (desiredType.equals(CodesetType.getByTypeCode(link.getCodeset()))) {
relatedValue = link.getValue();
break;
}
}
return relatedValue;
}
public Code getProductFor(String vaccineCvx, String vaccineMvx, String adminDate) {
List productCodes = getProductsFor(vaccineCvx, vaccineMvx);
if (productCodes.size() == 1) {
return productCodes.get(0);
} else if (productCodes.size() == 0) {
return null;
}
//at this point we know we have more than one product in our list.
// time to evaluate the dates to see which one is active.
if (logger.isTraceEnabled()) {
logger.trace("There is more than one product for cvx[" + vaccineCvx + "] + mvx[" + vaccineMvx
+ "] checking dates");
}
//We need to pick the most likely one.
if (adminDate != null) {
if (logger.isTraceEnabled()) {
logger.trace("Searching for product that is valid for admin date[" + adminDate + "]");
}
DateTime adminDT = parseDateTime(adminDate);
//At this point, we know there's two or more products.
for (Code productCode : productCodes) {
//Now we need to chose the right one based on the dates.
UseDate dates = productCode.getUseDate();
String beginning = dates.getNotBefore();
//Parse the dates...
DateTime bDT = parseDateTime(beginning);
if (logger.isTraceEnabled()) {
logger.trace("Must be after getNotBefore[" + beginning + "] ");
}
boolean isAfterBeginning = !isBeforeThis(adminDT, bDT);
if (logger.isTraceEnabled()) {
logger.trace("isAfterBeginning ? " + isAfterBeginning);
}
//We only care about the beginning date.
//This is very intentional, and it allows us to be flexible and return products where
//the admin date isn't exactly within the range, but its the closest one.
//This properly deals with gaps, and with overlapping ranges.
if (isAfterBeginning) {
return productCode;
}
}
//If we get here, there are multiple and the admin date is before ALL of the beginning dates
//so just return the last one in the list.
return productCodes.get(productCodes.size() - 1);
}
return null;
}
protected DateTime parseDateTime(String inDate) {
if (inDate != null) {
try {
return dtf.parseDateTime(inDate);
} catch (IllegalArgumentException iae) {
return null;
}
}
return null;
}
protected boolean isAfterThis(DateTime isThis, DateTime afterThis) {
if (isThis != null) {
boolean isAfterThis = (afterThis != null && isThis.isAfter(afterThis));
return isAfterThis;
}
return false;
}
protected boolean isBeforeThis(DateTime isThis, DateTime beforeThis) {
if (isThis != null) {
boolean isBefore = (beforeThis != null && isThis.isBefore(beforeThis));
return isBefore;
}
return false;
}
private Codebase base;
public CodeMap(Codebase mapthis) {
remap(mapthis);
}
public CodeMap() {
//default constructor.
}
public void remap(Codebase mapthis) {
this.base = mapthis;
if (logger.isTraceEnabled()) {
logger.trace("CodeBase Mapping!");
}
this.codeBaseMap = new HashMap>();
for (Codeset s : mapthis.getCodeset()) {
//Get the type for this set:
CodesetType typeCode = CodesetType.getByTypeCode(s.getType());
//Map the codeset:
if (logger.isInfoEnabled()) {
logger.info("Mapping codeset: " + s.getType());
}
Map codeMap = new HashMap();
for (Code c : s.getCode()) {
codeMap.put(c.getValue(), c);
if (logger.isTraceEnabled()) {
logger.trace(
" " + s.getType() + " value[" + c.getValue() + "] for code[" + c.getLabel()
+ "]");
}
}
this.codeBaseMap.put(typeCode, codeMap);
//Special Handling for NDC Codes. Add it to an additional CodesetType.
if (typeCode == CodesetType.VACCINATION_NDC_CODE_UNIT_OF_SALE
|| typeCode == CodesetType.VACCINATION_NDC_CODE_UNIT_OF_USE) {
Map ndcCodeMap = this.codeBaseMap.get(CodesetType.VACCINATION_NDC_CODE);
//The first NDC set will need to build a map.
if (ndcCodeMap == null) {
ndcCodeMap = new HashMap<>();
this.codeBaseMap.put(CodesetType.VACCINATION_NDC_CODE, ndcCodeMap);
}
ndcCodeMap.putAll(codeMap);
}
//Special Handling for Vaccine Product
if (typeCode == CodesetType.VACCINE_PRODUCT) {
for (Code productCode : s.getCode()) {
Reference ref = productCode.getReference();
if (ref != null) {
List links = ref.getLinkTo();
String productCvx = null;
String productMvx = null;
if (links != null) {
for (LinkTo link : links) {
String codeset = link.getCodeset();
switch (CodesetType.getByTypeCode(codeset)) {
case VACCINATION_CVX_CODE:
productCvx = link.getValue();
break;
case VACCINATION_MANUFACTURER_CODE:
productMvx = link.getValue();
break;
default:
break;
}
}
}
if (productCvx != null && productMvx != null) {
List products = productMap.get(productCvx + MAP_DELIMITER + productMvx);
if (products == null) {
products = new ArrayList();
productMap.put(productCvx + MAP_DELIMITER + productMvx, products);
}
products.add(productCode);
if (products.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace("SORTING PRODUCT: " + productCode.getValue());
}
Collections.sort(products, new CustomCodeDateComparator());
}
}
}
}
}
}
}
/**
* If the products both have a date, they will sort by that date. if either one doesn't have a date, they
* will be considered equal, and sort in the order they come in.
* @author Josh
*
*/
public class CustomCodeDateComparator implements Comparator {
public int compare(Code o1, Code o2) {
UseDate dates = o1.getUseDate();
UseDate dates2 = o2.getUseDate();
if (dates != null && dates2 != null) {
String date1 = dates.getNotBefore();
if (date1 == null) {
date1 = dates.getNotAfter();
}
String date2 = dates2.getNotBefore();
if (date2 == null) {
date2 = dates2.getNotAfter();
}
if (date2 != null && date1 != null) {
return date2.compareTo(date1);//Want a descending sort.
}
}
return 0;
}
}
public Code getCodeForCodeset(CodesetType c, String value) {
return this.getCodeForCodeset(c, value, Ops.Mapping.MAP);
}
Code checkVariants(CodesetType c, String value) {
if (StringUtils.isBlank(value)) {
return null;
}
//Sometimes bar code scanners introduce a 12th character into the code...
//let's try to fix it if that caused problems.
if (
(value.length() == 12 || value.length() == 14) //dashes or no dashes
&& (c == CodesetType.VACCINATION_NDC_CODE
|| c == CodesetType.VACCINATION_NDC_CODE_UNIT_OF_SALE
|| c == CodesetType.VACCINATION_NDC_CODE_UNIT_OF_USE )) {
return codeBaseMap.get(c).get(value.substring(1));
}
return null;
}
public Code getCodeForCodeset(CodesetType c, String value, Ops.Mapping mappingOption) {
Code code = null;
if (StringUtils.isBlank(value)) {
return code;
}
// 1. Get the codeset
Map codeSetMap = codeBaseMap.get(c);
if (codeSetMap != null) {
// 2. get the code
code = codeSetMap.get(value);
// 2.1 if the code isn't found, check for some known variations on expressing the codes.
if (code == null) {
code = checkVariants(c, value);
}
if (logger.isDebugEnabled()) {
logger.debug("found code: " + code);
if (code != null) {
logger.debug(code.getLabel() != null && code.getCodeStatus() != null ? " status: " + code.getCodeStatus().getStatus() : " status: null");
}
}
// 3. Check if it's mapped to something else by deprecation.
if (code != null && mappingOption == Ops.Mapping.MAP) {
CodeStatusValue status = CodeStatusValue.getBy(code.getCodeStatus());
if (status == CodeStatusValue.DEPRECATED
&& code.getCodeStatus().getDeprecated() != null
) {
String newValue = code.getCodeStatus().getDeprecated().getNewCodeValue();
if (logger.isDebugEnabled()) {
logger.debug("Mapping to: " + newValue);
}
code = codeSetMap.get(newValue);
}
}
}
return code;
}
public Code getFirstRelatedCodeForCodeIn(CodesetType codeTypeIn, String codeIn,
CodesetType codeTypeDesired) {
List relatedCodes = this.getRelatedCodesForCodeIn(codeTypeIn, codeIn, codeTypeDesired);
if (relatedCodes != null && relatedCodes.size() > 0) {
return relatedCodes.get(0);
}
return null;
}
/**
* This is a shorcut method. It gives access to the related codes
* without first getting a Code object.
* @param ct
* @param value
* @return
*/
public Map> getRelatedCodes(CodesetType ct, String value) {
Code code = this.getCodeForCodeset(ct, value);
Map> links = getRelatedCodes(code);
return links;
}
public List getRelatedCodesForCodeIn(CodesetType codeTypeIn, String codeIn,
CodesetType codeTypeDesired) {
Map> relatedCodes = this.getRelatedCodes(codeTypeIn, codeIn);
return relatedCodes.get(codeTypeDesired);
}
/**
* This returns the first related code found, if present
*
* @param code The code where the reference should be taken from
* @param relatedCodesetType The reference codeset that needs to be looked up
* @return
*/
public String getRelatedCodeValue(Code code, CodesetType relatedCodesetType) {
if (code != null && code.getReference() != null) {
List links = code.getReference().getLinkTo();
for (LinkTo link : links) {
if (link != null) {
String codesetType = link.getCodeset();
CodesetType type = CodesetType.getByTypeCode(codesetType);
if (type == relatedCodesetType) {
return link.getValue();
}
}
}
}
return null;
}
/**
* This returns the first related code found, if present
*
* @param code The code where the reference should be taken from
* @param relatedCodesetType The reference codeset that needs to be looked up
* @return
*/
public Code getRelatedCode(Code code, CodesetType relatedCodesetType) {
String relatedValue = getRelatedCodeValue(code, relatedCodesetType);
if (StringUtils.isNotBlank(relatedValue)) {
return this.getCodeForCodeset(relatedCodesetType, relatedValue);
}
return null;
}
/**
* Gets the list of codes defined as related to the code sent in.
* @param c
* @return Map of Types, and the associated codes.
*/
public Map> getRelatedCodes(Code c) {
Map> relatedCodes = new HashMap>();
if (c != null && c.getReference() != null) {
List links = c.getReference().getLinkTo();
for (LinkTo link : links) {
if (link != null) {
String codesetType = link.getCodeset();
CodesetType type = CodesetType.getByTypeCode(codesetType);
List codeList = relatedCodes.get(type);
if (codeList == null) {
codeList = new ArrayList();
relatedCodes.put(type, codeList);
}
Code relatedCode = this.getCodeForCodeset(type, link.getValue());
codeList.add(relatedCode);
}
}
}
return relatedCodes;
}
public Collection getCodesForTable(CodesetType c) {
Map codeSetMap = codeBaseMap.get(c);
if (codeSetMap == null)
{
return null;
}
return codeSetMap.values();
}
public List getCodesets() {
return base.getCodeset();
}
/**
* A helper method to compare two codes. The Code object is a generated class, so anything put there
* would get erased the next time its generated.
* @param code1
* @param code2
* @return
*/
public boolean codeEquals(Code code1, Code code2) {
if (code1.getConceptType() == null) {
if (code2.getConceptType() != null) {
return false;
}
} else if (!code1.getConceptType().equals(code2.getConceptType())) {
return false;
}
if (code1.getValue() == null) {
if (code2.getValue() != null) {
return false;
}
} else if (!code1.getValue().equals(code2.getValue())) {
return false;
}
return true;
}
}