package org.ohdsi.webapi.report; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.ohdsi.sql.SqlRender; import org.ohdsi.sql.SqlTranslate; import org.ohdsi.webapi.helper.ResourceHelper; import org.ohdsi.webapi.report.mapper.*; import org.ohdsi.webapi.source.Source; import org.ohdsi.webapi.source.SourceDaimon; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.jdbc.core.JdbcTemplate; import java.util.List; public class CDMResultsAnalysisRunner { private static final String BASE_SQL_PATH = "/resources/cdmresults/sql"; private static final Log log = LogFactory.getLog(CDMResultsAnalysisRunner.class); private static final String[] STANDARD_COLUMNS = new String[]{"results_database_schema", "vocab_database_schema", "cdm_database_schema"}; private static final String[] DRILLDOWN_COLUMNS = new String[]{"results_database_schema", "vocab_database_schema", "conceptId"}; private ObjectMapper mapper; private String sourceDialect; public CDMResultsAnalysisRunner(String sourceDialect) { this.sourceDialect = sourceDialect; mapper = new ObjectMapper(); } public CDMDashboard getDashboard(JdbcTemplate jdbcTemplate, Source source) { CDMDashboard dashboard = new CDMDashboard(); String summarySql = this.renderTranslateSql(BASE_SQL_PATH + "/report/person/population.sql", source); if (summarySql != null) { dashboard.setSummary(jdbcTemplate.query(summarySql, new CDMAttributeMapper())); } String ageAtFirstObsSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/observationperiod/ageatfirst.sql", source); if (ageAtFirstObsSql != null) { dashboard.setAgeAtFirstObservation(jdbcTemplate.query(ageAtFirstObsSql, new ConceptDistributionMapper())); } String genderSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/person/gender.sql", source); if (genderSql != null) { dashboard.setGender(jdbcTemplate.query(genderSql, new ConceptCountMapper())); } String cumulObsSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/observationperiod/cumulativeduration.sql", source); if (cumulObsSql != null) { dashboard.setCumulativeObservation(jdbcTemplate.query(cumulObsSql, new CumulativeObservationMapper())); } String obsByMonthSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/observationperiod/observedbymonth.sql", source); if (obsByMonthSql != null) { dashboard.setObservedByMonth(jdbcTemplate.query(obsByMonthSql, new MonthObservationMapper())); } return dashboard; } /** * Queries for CDM person results for the given source * * @param jdbcTemplate JDBCTemplate * @return CDMPersonSummary */ public CDMPersonSummary getPersonResults(JdbcTemplate jdbcTemplate, final Source source) { CDMPersonSummary person = new CDMPersonSummary(); String summarySql = this.renderTranslateSql(BASE_SQL_PATH + "/report/person/population.sql", source); if (summarySql != null) { person.setSummary(jdbcTemplate.query(summarySql, new CDMAttributeMapper())); } String genderSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/person/gender.sql", source); if (genderSql != null) { person.setGender(jdbcTemplate.query(genderSql, new ConceptCountMapper())); } String raceSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/person/race.sql", source); if (raceSql != null) { person.setRace(jdbcTemplate.query(raceSql, new ConceptCountMapper())); } String ethnicitySql = this.renderTranslateSql(BASE_SQL_PATH + "/report/person/ethnicity.sql", source); if (ethnicitySql != null) { person.setEthnicity(jdbcTemplate.query(ethnicitySql, new ConceptCountMapper())); } String yearOfBirthSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/person/yearofbirth_data.sql", source); if (yearOfBirthSql != null) { person.setYearOfBirth(jdbcTemplate.query(yearOfBirthSql, new ConceptDistributionMapper())); } String yearOfBirthStatsSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/person/yearofbirth_stats.sql", source); if (yearOfBirthStatsSql != null) { person.setYearOfBirthStats(jdbcTemplate.query(yearOfBirthStatsSql, new CohortStatsMapper())); } return person; } public CDMAchillesHeel getHeelResults(JdbcTemplate jdbcTemplate, Source source) { CDMAchillesHeel heel = new CDMAchillesHeel(); String achillesSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/achillesheel/sqlAchillesHeel.sql", source); if (achillesSql != null) { heel.setMessages(jdbcTemplate.query(achillesSql, new CDMAttributeMapper())); } return heel; } public CDMDataDensity getDataDensityResults(JdbcTemplate jdbcTemplate, Source source) { CDMDataDensity cdmDataDensity = new CDMDataDensity(); String conceptsPerPersonSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/datadensity/conceptsperperson.sql", source); if (conceptsPerPersonSql != null) { cdmDataDensity.setConceptsPerPerson(jdbcTemplate.query(conceptsPerPersonSql, new ConceptQuartileMapper())); } String recordsPerPersonSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/datadensity/recordsperperson.sql", source); if (recordsPerPersonSql != null) { cdmDataDensity.setRecordsPerPerson(jdbcTemplate.query(recordsPerPersonSql, new SeriesPerPersonMapper())); } String totalRecordsSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/datadensity/totalrecords.sql", source); if (totalRecordsSql != null) { cdmDataDensity.setTotalRecords(jdbcTemplate.query(totalRecordsSql, new SeriesPerPersonMapper())); } return cdmDataDensity; } public CDMDeath getDeathResults(JdbcTemplate jdbcTemplate, Source source) { CDMDeath cdmDeath = new CDMDeath(); String prevalenceByGenderAgeYearSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/death/sqlPrevalenceByGenderAgeYear.sql", source); if (prevalenceByGenderAgeYearSql != null) { cdmDeath.setPrevalenceByGenderAgeYear(jdbcTemplate.query(prevalenceByGenderAgeYearSql, new ConceptDecileMapper())); } String prevalenceByMonthSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/death/sqlPrevalenceByMonth.sql", source); if (prevalenceByMonthSql != null) { cdmDeath.setPrevalenceByMonth(jdbcTemplate.query(prevalenceByMonthSql, new PrevalanceMapper())); } String deathByTypeSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/death/sqlDeathByType.sql", source); if (deathByTypeSql != null) { cdmDeath.setDeathByType(jdbcTemplate.query(deathByTypeSql, new ConceptCountMapper())); } String ageAtDeathSql = this.renderTranslateSql(BASE_SQL_PATH + "/report/death/sqlAgeAtDeath.sql", source); if (ageAtDeathSql != null) { cdmDeath.setAgeAtDeath(jdbcTemplate.query(ageAtDeathSql, new ConceptQuartileMapper())); } return cdmDeath; } private String renderTranslateSql(String sqlPath, Source source) { return renderTranslateSql(sqlPath, null, source); } public ArrayNode getTreemap(JdbcTemplate jdbcTemplate, String domain, Source source) { ObjectMapper mapper = new ObjectMapper(); ArrayNode arrayNode = mapper.createArrayNode(); String sqlPath = BASE_SQL_PATH + "/report/" + domain.toLowerCase() + "/treemap.sql"; String sql = this.renderTranslateSql(sqlPath, source); if (sql != null) { List<JsonNode> list = jdbcTemplate.query(sql, new GenericRowMapper(mapper)); arrayNode.addAll(list); } return arrayNode; } public JsonNode getDrilldown(JdbcTemplate jdbcTemplate, final String domain, final int conceptId, Source source) { ObjectMapper mapper = new ObjectMapper(); ObjectNode objectNode = mapper.createObjectNode(); ClassLoader cl = this.getClass().getClassLoader(); ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(cl); String pattern = BASE_SQL_PATH + "/report/" + domain.toLowerCase() + "/drilldown/*.sql"; try { Resource[] resources = resolver.getResources(pattern); for (Resource resource : resources) { String fullSqlPath = resource.getURL().getPath(); int startIndex = fullSqlPath.indexOf(BASE_SQL_PATH); String sqlPath = fullSqlPath.substring(startIndex); String sql = this.renderTranslateSql(sqlPath, conceptId, source); if (sql != null) { List<JsonNode> l = jdbcTemplate.query(sql, new GenericRowMapper(mapper)); String analysisName = resource.getFilename().replace(".sql", ""); objectNode.putArray(analysisName).addAll(l); } } } catch (Exception e) { log.error(e); } return objectNode; } private String renderTranslateSql(String sqlPath, Integer conceptId, Source source) { String sql = null; String resultsTableQualifier = source.getTableQualifier(SourceDaimon.DaimonType.Results); String vocabularyTableQualifier = source.getTableQualifier(SourceDaimon.DaimonType.Vocabulary); String cdmTableQualifier = source.getTableQualifier(SourceDaimon.DaimonType.CDM); try { String[] cols; String[] colValues; if (conceptId != null) { cols = DRILLDOWN_COLUMNS; colValues = new String[]{resultsTableQualifier, vocabularyTableQualifier, String.valueOf(conceptId)}; } else { cols = STANDARD_COLUMNS; colValues = new String[]{resultsTableQualifier, vocabularyTableQualifier, cdmTableQualifier}; } sql = ResourceHelper.GetResourceAsString(sqlPath); sql = SqlRender.renderSql(sql, cols, colValues); sql = SqlTranslate.translateSql(sql, sourceDialect, source.getSourceDialect()); } catch (Exception e) { log.error(String.format("Unable to translate sql for %s", sql), e); } return sql; } }