diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d280a96..250c543 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,18 +15,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: '11' + java-version: '17' - name: Cache Local Maven Repo - uses: actions/cache@v2.1.2 + uses: actions/cache@v2 with: path: ~/.m2/repository - key: maven-repo + key: maven-repo-test-${{ hashFiles('pom.xml') }} - name: Cache SonarCloud packages uses: actions/cache@v1 @@ -54,18 +56,17 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v2 with: distribution: 'temurin' - java-version: '11' + java-version: '17' - name: Cache Local Maven Repo - uses: actions/cache@v2.1.2 + uses: actions/cache@v2 with: - path: | - ~/.m2/repository - key: maven-repo + path: ~/.m2/repository + key: maven-repo-build-${{ hashFiles('pom.xml') }} - name: Build with Maven run: mvn -B package -DskipTests -Dmaven.javadoc.skip=true diff --git a/Dockerfile b/Dockerfile index a5e1bdc..c4aa6f2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,24 +1,8 @@ -FROM openjdk:14-ea-alpine +FROM openjdk:17 -ENV SPRING_PROFILES_ACTIVE=docker -ARG JAR_FILE=target/icd10-dictionary.jar +COPY target/icd10-dictionary.jar /app/ -ENV ICD_DB_HOST=localhost -ENV ICD_DB_PORT=5432 -ENV ICD_DB_NAME=icd10 -ENV ICD_DB_USER=postgres -ENV ICD_DB_PASSWORD=password -ENV ICD_POOL_SIZE=30 +WORKDIR /app +USER 1001 -RUN apk update -RUN apk upgrade -RUN apk add bash -RUN apk add gettext - -COPY ${JAR_FILE} app.jar -COPY ./docker/start.sh . -RUN chmod +x ./start.sh -RUN mkdir config -COPY ./docker/docker.template.yml config - -CMD ["./start.sh"] +CMD ["java", "-jar", "icd10-dictionary.jar"] diff --git a/README.md b/README.md index 3d349f6..32f2121 100644 --- a/README.md +++ b/README.md @@ -10,28 +10,18 @@ http://localhost:8080/fhir/ValueSet/$expand?url=http://hl7.org/fhir/sid/icd-10-g ## Default parameters -| variable | Docker | default value | -|---------------------------|---------------------------|--------------------------| -| application port | | 8080 | -| database host | ICD_DB_HOST | localhost | -| database port | ICD_DB_PORT | 5432 | -| database name | ICD_DB_NAME | icd10 | -| database user | ICD_DB_USER | postgres | -| database password | ICD_DB_PASSWORD | password | -| pool size | ICD_POOL_SIZE | 30 | +| Env Var | Default Value | +|----------------------------|----------------------------------------| +| | 8080 | +| SPRING_DATASOURCE_URL | jdbc:postgresql://localhost:5432/icd10 | +| SPRING_DATASOURCE_USERNAME | icd10 | +| SPRING_DATASOURCE_PASSWORD | icd10 | ## Start postgres -For testing purposes one can start a postgres database with Docker using following comand: +For testing purposes one can start a postgres database with Docker using following command: ``` -docker network create -d bridge icd-net -docker run --name icd-postgres -d --network=icd-net -e POSTGRES_PASSWORD=password -p 5432:5432 postgres:alpine -``` -Then create a database by executing a bash and using PSQL -``` -docker exec -it icd-postgres bin/bash -psql -U postgres -CREATE DATABASE icd10; +docker run -e POSTGRES_DB=icd10 -e POSTGRES_USER=icd10 -e POSTGRES_PASSWORD=icd10 -p 5432:5432 postgres ``` ## Preparing data @@ -56,15 +46,9 @@ java -jar fhir-claml-0.0.1-SNAPSHOT.jar -valueset http://hl7.org/fhir/sid/icd-10-gm/vs ``` -4.) Run the ICD-10 dictionary (as executable jar) and load the data by using the endpoint "/api/v1/icd/load" with the file path to the FHIR .json-file as body - e.g. -``` -http://localhost:8080/api/v1/icd/load - -C:\Users\xyz\icd-service\codesystem-icd10gm-2020.json +4.) Run the ICD-10 dictionary (as executable jar) and load the data by using the endpoint "/api/v1/icd/load": ``` -Remark: When working with Docker the file must be copied to a suitable location inside the container -``` -docker cp C:\Users\xyz\icd-service\codesystem-icd10gm-2020.json [CONTAINER-ID]:/var/tmp/icd10 +curl -d @codesystem-example.json -H Content-Type:application/json http://localhost:8080/api/v1/icd/load ``` ## Docker @@ -75,16 +59,12 @@ docker build -t icd-dictionary . ``` The command for starting the container is something like ``` -docker run --rm -d -e "ICD_DB_HOST=icd-postgres" -p 8080:8080 --network=icd-net --name icd-dictionary icd-dictionary +docker run --rm -d -p 8080:8080 --name icd-dictionary icd-dictionary ``` -## Developers - -This project uses lombok. Though it is not neccessary it is recomended to install a suitable lombok plugin for your IDE (e.g. for IntelliJ Idea install https://plugins.jetbrains.com/plugin/6317-lombok). - ## License -Copyright 2020 The Samply Development Community +Copyright 2020 - 2021 The Samply Community 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 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..fa4b966 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3.2' +services: + postgres: + image: "postgres" + environment: + POSTGRES_DB: icd10 + POSTGRES_USER: icd10 + POSTGRES_PASSWORD: icd10 + volumes: + - "data:/var/lib/postgresql/data" + + icd-dictionary: + build: . + environment: + SPRING_DATASOURCE_URL: "jdbc:postgresql://postgres:5432/icd10" + ports: + - "8080:8080" + depends_on: + - postgres + +volumes: + data: diff --git a/docker/docker.template.yml b/docker/docker.template.yml deleted file mode 100644 index 39e97e4..0000000 --- a/docker/docker.template.yml +++ /dev/null @@ -1,6 +0,0 @@ -app: - datasource: - jdbc-url: jdbc:postgresql://${ICD_DB_HOST}:${ICD_DB_PORT}/${ICD_DB_NAME}?currentSchema=samply - username: ${ICD_DB_USER} - password: ${ICD_DB_PASSWORD} - pool-size: ${ICD_POOL_SIZE} diff --git a/docker/start.sh b/docker/start.sh deleted file mode 100644 index 14c9385..0000000 --- a/docker/start.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -echo 'started start.sh' -envsubst < ./config/docker.template.yml > ./config/application.yml - -exec java -jar app.jar diff --git a/pom.xml b/pom.xml index 3588ff6..f38ca4e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,171 +1,156 @@ - - 4.0.0 - - - de.samply - parent - 11.1.1 - - - icd10-dictionary - 0.4.0-SNAPSHOT - - icd10-dictionary - REST Service for ICD10 lookups - https://github.com/samply/maven-parent/icd10-dictionary - - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - - - - - Bjoern Kroll - Bjoern.Kroll@uni-luebeck.de - University Medical Center Schleswig-Holstein - http://www.uksh.de/en - - - - - 11 - 2.5.6 - samply - https://sonarcloud.io - - - - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - - - - + + 4.0.0 + + + de.samply + parent + 11.1.1 + + + icd10-dictionary + 0.4.0-SNAPSHOT + + icd10-dictionary + REST Service for ICD10 lookups + https://github.com/samply/maven-parent/icd10-dictionary + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + Bjoern Kroll + Bjoern.Kroll@uni-luebeck.de + University Medical Center Schleswig-Holstein + http://www.uksh.de/en + + + + + 2.5.6 + 3.1.2 + 3.3.0 + samply + https://sonarcloud.io + + + - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-jdbc - - - - org.postgresql - postgresql - runtime - - - - org.flywaydb - flyway-core - - - - - javax.json - javax.json-api - 1.1.4 - - - - - org.glassfish - javax.json - 1.1 - runtime - - - - - javax.json.bind - javax.json.bind-api - 1.0.0-RC2 - - - - - org.eclipse - yasson - 1.0.0-RC1 - runtime - - - - org.projectlombok - lombok - provided - - - - org.apache.commons - commons-lang3 - 3.10 - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.junit.vintage - junit-vintage-engine - - - + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + - - - icd10-dictionary - - - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - - - - - org.springframework.boot - spring-boot-maven-plugin - ${spring-boot.version} - - de.samply.icd10dictionary.Icd10DictionaryApplication - - - - - repackage - - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - 8 - - - - + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-jdbc + + + + org.postgresql + postgresql + runtime + + + + org.flywaydb + flyway-core + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.junit.vintage + junit-vintage-engine + + + + + + + icd10-dictionary + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${version.maven.plugin.checkstyle} + + + com.puppycrawl.tools + checkstyle + 9.1 + + + + google_checks.xml + UTF-8 + true + true + false + warning + + + + validate + validate + + check + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 17 + 17 + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + de.samply.icd10dictionary.Icd10DictionaryApplication + + + + + repackage + + + + + + diff --git a/src/main/java/de/samply/icd10dictionary/Icd10DictionaryApplication.java b/src/main/java/de/samply/icd10dictionary/Icd10DictionaryApplication.java index ec54db5..1706b84 100644 --- a/src/main/java/de/samply/icd10dictionary/Icd10DictionaryApplication.java +++ b/src/main/java/de/samply/icd10dictionary/Icd10DictionaryApplication.java @@ -3,6 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +/** + * Main entry point. + */ @SpringBootApplication public class Icd10DictionaryApplication { diff --git a/src/main/java/de/samply/icd10dictionary/api/IcdCodeRestController.java b/src/main/java/de/samply/icd10dictionary/api/IcdCodeRestController.java index 2d4e601..3ce6cd7 100644 --- a/src/main/java/de/samply/icd10dictionary/api/IcdCodeRestController.java +++ b/src/main/java/de/samply/icd10dictionary/api/IcdCodeRestController.java @@ -3,11 +3,11 @@ import de.samply.icd10dictionary.model.IcdCode; import de.samply.icd10dictionary.model.ValueSet; import de.samply.icd10dictionary.model.ValueSetEntry; +import de.samply.icd10dictionary.model.ValueSetExpansion; +import de.samply.icd10dictionary.service.CodeSystem; import de.samply.icd10dictionary.service.LoadIcdCodeService; import de.samply.icd10dictionary.service.SearchIcdCodeService; import java.util.List; -import java.util.stream.Collectors; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -18,6 +18,9 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +/** + * READ API. + */ @RestController @CrossOrigin public class IcdCodeRestController { @@ -40,23 +43,20 @@ public ResponseEntity check() { } /** - * Loads ICD-10 catalog from specified location 'clamlFileUri' on server. + * Loads ICD-10 catalog from specified {@code codeSystem}. * - * @param clamlFileUri Location of content file + * @param codeSystem the CodeSystem * @return ResponseEntity Http status */ @PostMapping("api/v1/icd/load") - public ResponseEntity loadFromFile(@RequestBody String clamlFileUri) { + public ResponseEntity load(@RequestBody CodeSystem codeSystem) { try { - LoadIcdCodeService.ErrorCode errorCode = this.loadIcdCodeService.load(clamlFileUri); + LoadIcdCodeService.ErrorCode errorCode = this.loadIcdCodeService.load(codeSystem); switch (errorCode) { case OK: break; - case FILE_NOT_FOUND: - return new ResponseEntity<>("File not found", HttpStatus.BAD_REQUEST); case DB_NOT_EMPTY: return new ResponseEntity<>("Database not empty", HttpStatus.CONFLICT); - case OTHER: default: return new ResponseEntity<>("Unspecified error", HttpStatus.BAD_REQUEST); } @@ -64,7 +64,7 @@ public ResponseEntity loadFromFile(@RequestBody String clamlFileUri) { return new ResponseEntity<>(e.getLocalizedMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } - return new ResponseEntity<>("File imported", HttpStatus.OK); + return new ResponseEntity<>("Data imported", HttpStatus.OK); } /** @@ -76,7 +76,7 @@ public ResponseEntity loadFromFile(@RequestBody String clamlFileUri) { */ @GetMapping("fhir/ValueSet/$expand") public ValueSet expand(@RequestParam String url, @RequestParam String filter) { - if (!StringUtils.equalsIgnoreCase(url, URL_ICD_10_GM)) { + if (!URL_ICD_10_GM.equalsIgnoreCase(url)) { return new ValueSet(); } List icdCodes = this.searchIcdCodeService.retrieveCodesByQueryText(filter); @@ -84,19 +84,8 @@ public ValueSet expand(@RequestParam String url, @RequestParam String filter) { } private ValueSet createValueSet(List icdCodes) { - ValueSet valueSet = new ValueSet(); - List entries = - icdCodes.stream() - .map( - icdCode -> { - ValueSetEntry entry = new ValueSetEntry(); - entry.setCode(icdCode.getCode()); - entry.setDisplay(icdCode.getDisplay()); - return entry; - }) - .collect(Collectors.toList()); - valueSet.getExpansion().setContains(entries); - - return valueSet; + return new ValueSet(new ValueSetExpansion(icdCodes.stream() + .map(icdCode -> new ValueSetEntry(icdCode.code(), icdCode.display())) + .toList())); } } diff --git a/src/main/java/de/samply/icd10dictionary/dao/IcdCodeDao.java b/src/main/java/de/samply/icd10dictionary/dao/IcdCodeDao.java index d1af85e..915b570 100644 --- a/src/main/java/de/samply/icd10dictionary/dao/IcdCodeDao.java +++ b/src/main/java/de/samply/icd10dictionary/dao/IcdCodeDao.java @@ -1,13 +1,15 @@ package de.samply.icd10dictionary.dao; import de.samply.icd10dictionary.model.IcdCode; - import java.util.List; import java.util.Optional; +/** + * Main DAO. + */ public interface IcdCodeDao { - int insert(IcdCode icdCode); + void insert(IcdCode icdCode); Optional selectIcdCodeByCode(String codeParam); diff --git a/src/main/java/de/samply/icd10dictionary/dao/IcdCodeDaoPostgres.java b/src/main/java/de/samply/icd10dictionary/dao/IcdCodeDaoPostgres.java index 8a61668..0113c4a 100644 --- a/src/main/java/de/samply/icd10dictionary/dao/IcdCodeDaoPostgres.java +++ b/src/main/java/de/samply/icd10dictionary/dao/IcdCodeDaoPostgres.java @@ -3,85 +3,76 @@ import de.samply.icd10dictionary.model.IcdCode; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; import java.util.Optional; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; - -@SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection"}) -@Repository("postgres") +/** + * Postgres implementation. + */ +@Repository public class IcdCodeDaoPostgres implements IcdCodeDao { private final JdbcTemplate jdbcTemplate; - @Autowired public IcdCodeDaoPostgres(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } @Override - public int insert(IcdCode icdCode) { - final String sql = - "INSERT INTO IcdCode (code, kind, display, definition, parentCode, childCodes) " - + "VALUES (?, ?, ?, ?, ?, ?)"; + public void insert(IcdCode icdCode) { this.jdbcTemplate.update( - sql, - icdCode.getCode(), - icdCode.getKind(), - icdCode.getDisplay(), - icdCode.getDefinition(), - icdCode.getParentCode(), - icdCode.getChildCodes()); - return 0; + "INSERT INTO IcdCode (code, kind, display, definition, parentCode, childCodes) " + + "VALUES (?, ?, ?, ?, ?, ?)", + icdCode.code(), + icdCode.kind(), + icdCode.display(), + icdCode.definition(), + icdCode.parentCode(), + icdCode.childCodes()); } @Override public Optional selectIcdCodeByCode(String codeParam) { - final String sql = - "SELECT code, kind, display, definition, parentCode, childCodes " - + "FROM IcdCode " - + "WHERE code = ?"; IcdCode icdCode = this.jdbcTemplate.queryForObject( - sql, - new Object[] {codeParam}, - new int[] {1}, + "SELECT code, kind, display, definition, parentCode, childCodes " + + "FROM IcdCode " + + "WHERE code = ?", + new Object[]{codeParam}, + new int[]{1}, ((resultSet, i) -> createIcdCode(resultSet))); return Optional.ofNullable(icdCode); } @Override public int deleteByCode(String code) { - final String sql = "DELETE FROM IcdCode WHERE id = ?"; - this.jdbcTemplate.update(sql, code); + this.jdbcTemplate.update("DELETE FROM IcdCode WHERE id = ?", code); return 0; } @Override public int deleteAll() { - final String sql = "DELETE FROM IcdCode"; - this.jdbcTemplate.update(sql); + this.jdbcTemplate.update("DELETE FROM IcdCode"); return 0; } @Override public int count() { - final String sql = "SELECT count(*) FROM IcdCode "; - List countList = this.jdbcTemplate.query(sql, (resultSet, i) -> resultSet.getInt(1)); + List countList = this.jdbcTemplate.query( + "SELECT count(*) FROM IcdCode ", + (resultSet, i) -> resultSet.getInt(1)); return countList.get(0); } @Override public List retrieveCodesByQueryText(String queryText) { - if (StringUtils.isBlank(queryText)) { - return new ArrayList<>(); + if (queryText == null || queryText.isBlank()) { + return List.of(); } - final String sql = + return this.jdbcTemplate.query( "SELECT code, kind, display, definition, parentCode, childCodes " + "FROM IcdCode " + "WHERE childCodes = ''" @@ -90,20 +81,20 @@ public List retrieveCodesByQueryText(String queryText) { + " array_to_string(" + " array(select unnest || ':*' " + " from unnest(" - + " regexp_split_to_array(plainto_tsquery('german', '" + queryText + "')::text, " + + " regexp_split_to_array(plainto_tsquery('german', ?)::text, " + " '\\s&\\s')" + " )" + " ), ' & '" - + " )::tsquery)"; - return this.jdbcTemplate.query(sql, new String[]{"%" + queryText + "%"}, new int[]{1}, - ((resultSet, i) -> createIcdCode(resultSet))); + + " )::tsquery)", + ((resultSet, i) -> createIcdCode(resultSet)), + "%" + queryText + "%"); } @Override public List retrieveAll() { - final String sql = - "SELECT code, kind, display, definition, parentCode, childCodes FROM IcdCode "; - return this.jdbcTemplate.query(sql, ((resultSet, i) -> createIcdCode(resultSet))); + return this.jdbcTemplate.query( + "SELECT code, kind, display, definition, parentCode, childCodes FROM IcdCode ", + ((resultSet, i) -> createIcdCode(resultSet))); } private IcdCode createIcdCode(ResultSet resultSet) throws SQLException { diff --git a/src/main/java/de/samply/icd10dictionary/datasource/DatasourceConfiguration.java b/src/main/java/de/samply/icd10dictionary/datasource/DatasourceConfiguration.java deleted file mode 100644 index 20f5a6d..0000000 --- a/src/main/java/de/samply/icd10dictionary/datasource/DatasourceConfiguration.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.samply.icd10dictionary.datasource; - -import javax.sql.DataSource; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.jdbc.DataSourceBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - - -@Configuration -public class DatasourceConfiguration { - - @Bean(name = "icdDataSource") - @ConfigurationProperties("app.datasource") - public DataSource icdDataSource() { - return DataSourceBuilder.create().build(); - } -} diff --git a/src/main/java/de/samply/icd10dictionary/model/IcdCode.java b/src/main/java/de/samply/icd10dictionary/model/IcdCode.java index c1c93f4..6469adc 100644 --- a/src/main/java/de/samply/icd10dictionary/model/IcdCode.java +++ b/src/main/java/de/samply/icd10dictionary/model/IcdCode.java @@ -1,16 +1,9 @@ package de.samply.icd10dictionary.model; -import lombok.AllArgsConstructor; -import lombok.Data; +/** + * A row of the IcdCode database table. + */ +public record IcdCode(String code, String kind, String display, String definition, + String parentCode, String childCodes) { -@Data -@AllArgsConstructor -public class IcdCode { - - private String code; - private String kind; - private String display; - private String definition; - private String parentCode; - private String childCodes; } diff --git a/src/main/java/de/samply/icd10dictionary/model/ValueSet.java b/src/main/java/de/samply/icd10dictionary/model/ValueSet.java index c52a558..fe133f5 100644 --- a/src/main/java/de/samply/icd10dictionary/model/ValueSet.java +++ b/src/main/java/de/samply/icd10dictionary/model/ValueSet.java @@ -1,10 +1,11 @@ package de.samply.icd10dictionary.model; -import lombok.Data; -import lombok.NoArgsConstructor; +/** + * FHIR ValueSet. + */ +public record ValueSet(ValueSetExpansion expansion) { -@Data -@NoArgsConstructor -public class ValueSet { - private ValueSetExpansion expansion = new ValueSetExpansion(); + public ValueSet() { + this(new ValueSetExpansion()); + } } diff --git a/src/main/java/de/samply/icd10dictionary/model/ValueSetEntry.java b/src/main/java/de/samply/icd10dictionary/model/ValueSetEntry.java index 5fdb17c..3e76920 100644 --- a/src/main/java/de/samply/icd10dictionary/model/ValueSetEntry.java +++ b/src/main/java/de/samply/icd10dictionary/model/ValueSetEntry.java @@ -1,11 +1,8 @@ package de.samply.icd10dictionary.model; -import lombok.Data; -import lombok.NoArgsConstructor; +/** + * FHIR ValueSet. + */ +public record ValueSetEntry(String code, String display) { -@Data -@NoArgsConstructor -public class ValueSetEntry { - private String code; - private String display; } diff --git a/src/main/java/de/samply/icd10dictionary/model/ValueSetExpansion.java b/src/main/java/de/samply/icd10dictionary/model/ValueSetExpansion.java index f46277d..1477c5c 100644 --- a/src/main/java/de/samply/icd10dictionary/model/ValueSetExpansion.java +++ b/src/main/java/de/samply/icd10dictionary/model/ValueSetExpansion.java @@ -1,13 +1,13 @@ package de.samply.icd10dictionary.model; -import java.util.ArrayList; import java.util.List; -import lombok.Data; -import lombok.NoArgsConstructor; +/** + * FHIR ValueSet. + */ +public record ValueSetExpansion(List contains) { -@Data -@NoArgsConstructor -public class ValueSetExpansion { - private List contains = new ArrayList<>(); + ValueSetExpansion() { + this(List.of()); + } } diff --git a/src/main/java/de/samply/icd10dictionary/service/CodeSystem.java b/src/main/java/de/samply/icd10dictionary/service/CodeSystem.java index 1f8af34..7adc9fc 100644 --- a/src/main/java/de/samply/icd10dictionary/service/CodeSystem.java +++ b/src/main/java/de/samply/icd10dictionary/service/CodeSystem.java @@ -1,13 +1,14 @@ package de.samply.icd10dictionary.service; -import java.util.ArrayList; import java.util.List; -import lombok.Data; -import lombok.NoArgsConstructor; +/** + * FHIR CodeSystem. + */ +public record CodeSystem(List concept) { -@Data -@NoArgsConstructor -public class CodeSystem { - private List concept = new ArrayList<>(); + @Override + public List concept() { + return concept == null ? List.of() : concept; + } } diff --git a/src/main/java/de/samply/icd10dictionary/service/Concept.java b/src/main/java/de/samply/icd10dictionary/service/Concept.java index c7ca951..9fe9cb0 100644 --- a/src/main/java/de/samply/icd10dictionary/service/Concept.java +++ b/src/main/java/de/samply/icd10dictionary/service/Concept.java @@ -1,16 +1,14 @@ package de.samply.icd10dictionary.service; -import java.util.ArrayList; import java.util.List; -import lombok.Data; -import lombok.NoArgsConstructor; +/** + * FHIR CodeSystem. + */ +public record Concept(String code, String display, String definition, List property) { -@Data -@NoArgsConstructor -public class Concept { - private String code; - private String display; - private String definition; - private List property = new ArrayList<>(); + @Override + public List property() { + return property == null ? List.of() : property; + } } diff --git a/src/main/java/de/samply/icd10dictionary/service/LoadIcdCodeService.java b/src/main/java/de/samply/icd10dictionary/service/LoadIcdCodeService.java index fc73640..ddf6d00 100644 --- a/src/main/java/de/samply/icd10dictionary/service/LoadIcdCodeService.java +++ b/src/main/java/de/samply/icd10dictionary/service/LoadIcdCodeService.java @@ -2,96 +2,88 @@ import de.samply.icd10dictionary.dao.IcdCodeDao; import de.samply.icd10dictionary.model.IcdCode; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.util.Collection; import java.util.Optional; import java.util.stream.Collectors; -import javax.json.bind.Jsonb; -import javax.json.bind.JsonbBuilder; import org.apache.tomcat.util.buf.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +/** + * Data load service. + */ @Service public class LoadIcdCodeService { + private static final Logger logger = LoggerFactory.getLogger(LoadIcdCodeService.class); + private final IcdCodeDao icdCodeDao; - @Autowired - public LoadIcdCodeService(@Qualifier("postgres") IcdCodeDao icdCodeDao) { + public LoadIcdCodeService(IcdCodeDao icdCodeDao) { this.icdCodeDao = icdCodeDao; } /** - * Reads Codesystem (CLAML) from the local filesystem and writes codes to database. + * Imports the given CodeSystem into the database. * - * @param filename the filename of the codesystem - * @return ErrorCode + * @param codeSystem the CodeSystem + * @return {@code DB_NOT_EMPTY} if the database contains already some codes */ - public ErrorCode load(String filename) { - if (this.icdCodeDao.count() > 0) { + public ErrorCode load(CodeSystem codeSystem) { + if (icdCodeDao.count() > 0) { return ErrorCode.DB_NOT_EMPTY; } - Jsonb jsonb = JsonbBuilder.create(); - CodeSystem codeSystem; - try { - codeSystem = jsonb.fromJson(new FileInputStream(filename), CodeSystem.class); - } catch (FileNotFoundException e) { - return ErrorCode.FILE_NOT_FOUND; - } - createIcdCodes(codeSystem); return ErrorCode.OK; } private void createIcdCodes(CodeSystem codeSystem) { - codeSystem.getConcept().forEach(concept -> this.icdCodeDao.insert(createIcdCode(concept))); + codeSystem.concept() + .stream().map(LoadIcdCodeService::createIcdCode) + .forEach(optionalIcdCode -> { + if (optionalIcdCode.isPresent()) { + logger.debug("load ICD cod {}", optionalIcdCode.get()); + icdCodeDao.insert(optionalIcdCode.get()); + } else { + logger.warn("skip non-buildable ICD code"); + } + }); } - private IcdCode createIcdCode(Concept concept) { - return new IcdCode( - concept.getCode(), - determineKind(concept), - concept.getDefinition(), - concept.getDisplay(), - determineParentCode(concept), - StringUtils.join(determineChildCodes(concept), ',')); + private static Optional createIcdCode(Concept concept) { + return findProperty(concept, "kind") + .map(kind -> new IcdCode( + concept.code(), + kind, + concept.definition(), + concept.display(), + findProperty(concept, "parent").orElse(null), + StringUtils.join(determineChildCodes(concept), ','))); } - private Collection determineChildCodes(Concept concept) { - return concept.getProperty().stream() - .filter(property -> property.getCode().equals("child")) - .map(Property::getValueCode) + private static Collection determineChildCodes(Concept concept) { + return concept.property().stream() + .filter(property -> property.code().equals("child")) + .map(Property::valueCode) .collect(Collectors.toList()); } - private String determineParentCode(Concept concept) { - return findProperty(concept, "parent"); - } - - private String determineKind(Concept concept) { - return findProperty(concept, "kind"); - } - - private String findProperty(Concept concept, String propertyCode) { - Optional parentPropertyMayBe = - concept.getProperty().stream() - .filter(property -> property.getCode().equals(propertyCode)) - .findFirst(); - if (parentPropertyMayBe.isEmpty()) { - return null; - } else { - return parentPropertyMayBe.get().getValueCode(); - } + private static Optional findProperty(Concept concept, String propertyCode) { + return concept.property().stream() + .filter(property -> property.code().equals(propertyCode)) + .findFirst() + .map(Property::valueCode); } + /** + * Error codes. + */ public enum ErrorCode { OK, FILE_NOT_FOUND, DB_NOT_EMPTY, - OTHER; + OTHER } } diff --git a/src/main/java/de/samply/icd10dictionary/service/Property.java b/src/main/java/de/samply/icd10dictionary/service/Property.java index 6307874..faa4d2c 100644 --- a/src/main/java/de/samply/icd10dictionary/service/Property.java +++ b/src/main/java/de/samply/icd10dictionary/service/Property.java @@ -1,12 +1,8 @@ package de.samply.icd10dictionary.service; -import lombok.Data; -import lombok.NoArgsConstructor; +/** + * FHIR CodeSystem. + */ +public record Property(String code, String valueString, String valueCode) { -@Data -@NoArgsConstructor -public class Property { - private String code; - private String valueString; - private String valueCode; } diff --git a/src/main/java/de/samply/icd10dictionary/service/SearchIcdCodeService.java b/src/main/java/de/samply/icd10dictionary/service/SearchIcdCodeService.java index bcacf4b..86ee9dd 100644 --- a/src/main/java/de/samply/icd10dictionary/service/SearchIcdCodeService.java +++ b/src/main/java/de/samply/icd10dictionary/service/SearchIcdCodeService.java @@ -3,17 +3,17 @@ import de.samply.icd10dictionary.dao.IcdCodeDao; import de.samply.icd10dictionary.model.IcdCode; import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; +/** + * Main Service. + */ @Service public class SearchIcdCodeService { private final IcdCodeDao icdCodeDao; - @Autowired - public SearchIcdCodeService(@Qualifier("postgres") IcdCodeDao icdCodeDao) { + public SearchIcdCodeService(IcdCodeDao icdCodeDao) { this.icdCodeDao = icdCodeDao; } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 5db3e9d..8292796 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,5 @@ -app: +spring: datasource: - jdbc-url: jdbc:postgresql://localhost:5432/icd10?currentSchema=samply - username: postgres - password: password - pool-size: 30 + url: jdbc:postgresql://localhost:5432/icd10?currentSchema=samply + username: icd10 + password: icd10