
com.imsweb.algorithms.causespecific.CauseSpecificUtils Maven / Gradle / Ivy
/*
* Copyright (C) 2014 Information Management Services, Inc.
*/
package com.imsweb.algorithms.causespecific;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import com.imsweb.algorithms.seersiterecode.SeerSiteRecodeUtils;
/**
* This class is used to calculate the cause specific death classification variables. More information can be found here:
* http://seer.cancer.gov/causespecific/
*
* Author: Sewbesew Bekele
* Date: Feb 7, 2014
*/
public final class CauseSpecificUtils {
public static final String PROP_PRIMARY_SITE = "primarySite";
public static final String PROP_SEQ_NUM_CENTRAL = "sequenceNumberCentral";
public static final String PROP_HISTOLOGY_ICDO3 = "histologyIcdO3";
public static final String PROP_DOLC_YEAR = "dateOfLastContactYear";
public static final String PROP_COD = "causeOfDeath";
public static final String PROP_ICD_REVISION_NUM = "icdRevisionNumber";
public static final String PROP_VITAL_STATUS = "vitalStatus";
//values for cause specific and cause others
public static final String ALIVE_OR_DEAD_OF_OTHER_CAUSES = "0";
public static final String DEAD = "1";
public static final String NA_NOT_FIRST_TUMOR = "9";
//lookup for tables
private static List _DATA_SITE_SPECIFIC = new ArrayList<>();
/**
* Calculates cause specific and cause other death classification values for the provided record.
*
* The provided record doesn't need to contain all the input variables, but the algorithm wil use the following ones:
*
* - sequenceNumberCentral (#380)
* - icdRevisionNumber (#1920)
* - causeOfDeath (#1910)
* - primarySite (#400)
* - behaviorIcdO3 (#523)
* - dateOfLastContactYear (#1750)
* - vitalStatus (#1760)
*
* All those properties are defined as constants in this class.
*
* @param record a map of properties representing a NAACCR line
* @return the computed cause specific and cause other death classification values. The out put values are:
*
* - ALIVE OR DEAD OF OTHER CAUSES = "0"
* - DEAD = "1"
* - N/A NOT FIRST TUMOR = "2"
*
*/
public static CauseSpecificResultDto computeCauseSpecific(Map record) {
return computeCauseSpecific(record, Calendar.getInstance().get(Calendar.YEAR));
}
/**
* Calculates cause specific and cause other death classification values for the provided input dto.
*
* The input dto may have the following parameters:
*
* - _sequenceNumberCentral
* - _icdRevisionNumber
* - _causeOfDeath
* - _primarySite
* - _behaviorIcdO3
* - _dateOfLastContactYear
* - _vitalStatus
*
*
* @param input an input dto which has the fields used to compute cause specific values as parameter.
* @return the computed cause specific and cause other death classification values. The out put values are:
*
* - ALIVE OR DEAD OF OTHER CAUSES = "0"
* - DEAD = "1"
* - N/A NOT FIRST TUMOR = "2"
*
*/
public static CauseSpecificResultDto computeCauseSpecific(CauseSpecificInputDto input) {
return computeCauseSpecific(input, Calendar.getInstance().get(Calendar.YEAR));
}
/**
* Calculates cause specific and cause other death classification values for the provided record and cut off year.
*
* The provided record doesn't need to contain all the input variables, but the algorithm wil use the following ones:
*
* - sequenceNumberCentral (#380)
* - icdRevisionNumber (#1920)
* - causeOfDeath (#1910)
* - primarySite (#400)
* - behaviorIcdO3 (#523)
* - dateOfLastContactYear (#1750)
* - vitalStatus (#1760)
*
* All those properties are defined as constants in this class.
*
* @param record a map of properties representing a NAACCR line
* @param cutOffYear submission year, if date of last contact is beyond this year, patient is assumed alive.
* @return the computed cause specific and cause other death classification values. The out put values are:
*
* - ALIVE OR DEAD OF OTHER CAUSES = "0"
* - DEAD = "1"
* - N/A NOT FIRST TUMOR = "2"
*
*/
public static CauseSpecificResultDto computeCauseSpecific(Map record, int cutOffYear) {
CauseSpecificInputDto input = new CauseSpecificInputDto();
input.setPrimarySite(record.get(PROP_PRIMARY_SITE));
input.setSequenceNumberCentral(record.get(PROP_SEQ_NUM_CENTRAL));
input.setHistologyIcdO3(record.get(PROP_HISTOLOGY_ICDO3));
input.setDateOfLastContactYear(record.get(PROP_DOLC_YEAR));
input.setCauseOfDeath(record.get(PROP_COD));
input.setIcdRevisionNumber(record.get(PROP_ICD_REVISION_NUM));
input.setVitalStatus(record.get(PROP_VITAL_STATUS));
return computeCauseSpecific(input, cutOffYear);
}
/**
* Calculates cause specific and cause other death classification values for the provided input dto and cut off year.
*
* The input dto may have the following parameters:
*
* - _sequenceNumberCentral
* - _icdRevisionNumber
* - _causeOfDeath
* - _primarySite
* - _behaviorIcdO3
* - _dateOfLastContactYear
* - _vitalStatus
*
*
* @param input an input dto which has the fields used to compute cause specific values as parameter.
* @param cutOffYear submission year, if date of last contact is beyond this year, patient is assumed alive.
* @return the computed cause specific and cause other death classification values. The out put values are:
*
* - ALIVE OR DEAD OF OTHER CAUSES = "0"
* - DEAD = "1"
* - N/A NOT FIRST TUMOR = "2"
*
*/
public static CauseSpecificResultDto computeCauseSpecific(CauseSpecificInputDto input, int cutOffYear) {
CauseSpecificResultDto result = new CauseSpecificResultDto();
int seq;
if ("00".equals(input.getSequenceNumberCentral()))
seq = 0;
else if ("01".equals(input.getSequenceNumberCentral()))
seq = 1;
else {
result.setCauseSpecificDeathClassification(NA_NOT_FIRST_TUMOR);
result.setCauseOtherDeathClassification(NA_NOT_FIRST_TUMOR);
return result;
}
int dolc = NumberUtils.toInt(input.getDateOfLastContactYear(), 9999);
String icd = input.getIcdRevisionNumber();
//If patient is alive at last fup before submission
if ("0".equals(icd) || (dolc != 9999 && dolc > cutOffYear)) {
result.setCauseSpecificDeathClassification(ALIVE_OR_DEAD_OF_OTHER_CAUSES);
result.setCauseOtherDeathClassification(ALIVE_OR_DEAD_OF_OTHER_CAUSES);
return result;
}
if (input.getCauseOfDeath() == null || input.getCauseOfDeath().length() < 3) {
result.setCauseSpecificDeathClassification(ALIVE_OR_DEAD_OF_OTHER_CAUSES);
//if dead
if ("4".equals(input.getVitalStatus()))
result.setCauseOtherDeathClassification(DEAD);
else
result.setCauseOtherDeathClassification(ALIVE_OR_DEAD_OF_OTHER_CAUSES);
return result;
}
int hist = NumberUtils.toInt(input.getHistologyIcdO3(), -1);
String cod = input.getCauseOfDeath().toUpperCase();
String cod3dig = cod.substring(0, 3);
String recode = SeerSiteRecodeUtils.calculateSiteRecode(input.getPrimarySite(), input.getHistologyIcdO3());
// first do all of the non-site-specific checks, some of the condition could be added to the text file which represents the tables. But I decided to use the same file and
// same structure of code as SAS.
int causeSpecific = 0;
if (seq == 0) {
if ("8".equals(icd)) {
//any cancer 140-239, coded as dead;
if ("140".compareTo(cod3dig) <= 0 && "239".compareTo(cod3dig) >= 0)
causeSpecific = 1;
}
else if ("9".equals(icd)) {
//any cancer 140-239 and hiv & malignant 042.2, coded as dead
if (("140".compareTo(cod3dig) <= 0 && "239".compareTo(cod3dig) >= 0) || "0422".equals(cod))
causeSpecific = 1;
}
else if ("1".equals(icd)) {
//any cancer C00-D489 & AIDS & cancer B210-B219, coded as dead
if (("C00".compareTo(cod3dig) <= 0 && "D489".compareTo(cod) >= 0) || "B21".equals(cod3dig))
causeSpecific = 1;
//The last 4 rows (special cases) under miscellaneous, D36, D45-47 for histology 9950, 9960-9964, 9980-9989 are included in the above condition
}
}
else {
if ("8".equals(icd)) {
//unknown primary & secondary site 199, coded as dead;
if ("199".equals(cod3dig))
causeSpecific = 1;
//Melanoma of any site (8720-8799) with a cause of death of 172, 216.9, or 232.2 is coded as 'dead' (footnote)
else if (hist >= 8720 && hist <= 8799 && ("172".equals(cod3dig) || "2169".equals(cod) || "2322".equals(cod)))
causeSpecific = 1;
}
else if ("9".equals(icd)) {
//unknown primary & secondary site 199, coded as dead;
if ("199".equals(cod3dig))
causeSpecific = 1;
//Melanoma of any site (8720-8799) with a cause of death of 172, 216, or 232 is coded as 'dead' (footnote)
else if (hist >= 8720 && hist <= 8799 && ("172".equals(cod3dig) || "216".equals(cod3dig) || "232".equals(cod3dig)))
causeSpecific = 1;
}
else if ("1".equals(icd)) {
//Secondary other specified C798, unknown primary C80, multiple cancer C97, neoplasm nos D489, coded as dead;
if ("C798".equals(cod) || "C80".equals(cod3dig) || "C97".equals(cod3dig) || "D489".equals(cod))
causeSpecific = 1;
//Melanoma of any site (8720-8799) with a cause of death of C43, D03 or D22 is coded as 'dead'. (footnote)
else if (hist >= 8720 && hist <= 8799 && ("C43".equals(cod3dig) || "D03".equals(cod3dig) || "D22".equals(cod3dig)))
causeSpecific = 1;
//The last 4 rows (special cases) under miscellaneous, C77, C81-96,D36,D45-47 for histology 9950, 9960-9964, 9980-9989 and C00-D48, D619 for other
else if (hist == 9950 || (hist >= 9960 && hist <= 9964) || (hist >= 9980 && hist <= 9989)) {
if ("C77".equals(cod3dig) || ("C81".compareTo(cod3dig) <= 0 && "C96".compareTo(cod3dig) >= 0) || "D36".equals(cod3dig) || ("D45".compareTo(cod3dig) <= 0 && "D47".compareTo(cod3dig)
>= 0))
causeSpecific = 1;
}
else if ("37000".equals(recode) && (("C00".compareTo(cod3dig) <= 0 && "D489".compareTo(cod) >= 0) || "D619".equals(cod)))
causeSpecific = 1;
}
}
//If we get a result, stop. otherwise continue to site specific
if (causeSpecific == 1) {
result.setCauseSpecificDeathClassification(DEAD);
result.setCauseOtherDeathClassification(ALIVE_OR_DEAD_OF_OTHER_CAUSES);
return result;
}
for (CauseSpecificDataDto obj : getData())
if (obj.doesMatchThisRow(input.getIcdRevisionNumber(), String.valueOf(seq), recode, cod)) {
result.setCauseSpecificDeathClassification(DEAD);
result.setCauseOtherDeathClassification(ALIVE_OR_DEAD_OF_OTHER_CAUSES);
return result;
}
//If not found in the table cause-specific = '0' and cause- other = '1'
result.setCauseSpecificDeathClassification(ALIVE_OR_DEAD_OF_OTHER_CAUSES);
result.setCauseOtherDeathClassification(DEAD);
return result;
}
protected static synchronized List getData() {
if (_DATA_SITE_SPECIFIC.isEmpty()) {
LineNumberReader reader = null;
try {
reader = new LineNumberReader(new InputStreamReader(Thread.currentThread().getContextClassLoader().getResourceAsStream("causespecific/data.txt"), "US-ASCII"));
reader.readLine(); //skip first line
String line = reader.readLine();
while (line != null) {
_DATA_SITE_SPECIFIC.add(new CauseSpecificDataDto(StringUtils.split(line, ';')));
line = reader.readLine();
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
finally {
try {
if (reader != null)
reader.close();
}
catch (IOException e) {
//ignored, we tried our best
}
}
}
return _DATA_SITE_SPECIFIC;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy