From 37314fca05468a3bfa8d4c3d23584586463a97fa Mon Sep 17 00:00:00 2001 From: Paul-Christian Volkmer Date: Mon, 7 Sep 2026 15:15:19 +0200 Subject: feat!: only add level 4/5 ATC codes (#295) ATC codes like `L`, `L01`, `L01E` are not imported any more and cannot be used as ATC code from database. These codes (from database) will be treated as unregistered code instead.--- src/main/java/dev/dnpm/oshelper/atc/AtcCode.java | 4 +-- .../oshelper/atc/services/AgentCodeService.java | 9 ++++++ .../oshelper/atc/services/CsvAgentCodeService.java | 25 +++++++++++---- .../atc/services/OnkostarAgentCodeService.java | 37 ++++++++++++++++++++++ .../java/dev/dnpm/oshelper/atc/AtcCodeTest.java | 8 +++-- .../dnpm/oshelper/atc/CsvAgentCodeServiceTest.java | 9 ++++++ 6 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/main/java/dev/dnpm/oshelper/atc/AtcCode.java b/src/main/java/dev/dnpm/oshelper/atc/AtcCode.java index beff5bf..ba42737 100644 --- a/src/main/java/dev/dnpm/oshelper/atc/AtcCode.java +++ b/src/main/java/dev/dnpm/oshelper/atc/AtcCode.java @@ -91,7 +91,7 @@ public class AtcCode implements AgentCode { } /** - * Checks for usable ATC code starting at level 2 + * Checks for usable ATC code starting at level 4 * * @param code Code to be checked * @return Will return true if code matches ATC code scheme @@ -99,6 +99,6 @@ public class AtcCode implements AgentCode { public static boolean isAtcCode(String code) { return null != code && !code.isBlank() - && code.matches("[ABCDGHJLMNPRSV][0-2][1-9]([A-Z]([A-Z](\\d{2})?)?)?"); + && code.matches("[ABCDGHJLMNPRSV][0-2][1-9][A-Z][A-Z](\\d{2})?"); } } diff --git a/src/main/java/dev/dnpm/oshelper/atc/services/AgentCodeService.java b/src/main/java/dev/dnpm/oshelper/atc/services/AgentCodeService.java index f1dc2e2..6540023 100644 --- a/src/main/java/dev/dnpm/oshelper/atc/services/AgentCodeService.java +++ b/src/main/java/dev/dnpm/oshelper/atc/services/AgentCodeService.java @@ -25,6 +25,7 @@ import dev.dnpm.oshelper.atc.AgentCode; import java.util.Comparator; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; import org.jspecify.annotations.NullMarked; @@ -100,4 +101,12 @@ public interface AgentCodeService { * @return A list with agent codes */ List findAgentCodes(String query, int size); + + /** + * Queries source for agents with code matching code string. + * + * @param code The code string + * @return An optional agent code if found + */ + Optional getAgentCode(String code); } diff --git a/src/main/java/dev/dnpm/oshelper/atc/services/CsvAgentCodeService.java b/src/main/java/dev/dnpm/oshelper/atc/services/CsvAgentCodeService.java index abb006b..e355034 100644 --- a/src/main/java/dev/dnpm/oshelper/atc/services/CsvAgentCodeService.java +++ b/src/main/java/dev/dnpm/oshelper/atc/services/CsvAgentCodeService.java @@ -27,6 +27,7 @@ import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.csv.CSVFormat; import org.slf4j.Logger; @@ -74,6 +75,17 @@ public class CsvAgentCodeService implements AgentCodeService { return resultStream.collect(Collectors.toList()); } + /** + * Queries source for agents with code matching code string. + * + * @param code The code string + * @return An optional agent code if found + */ + @Override + public Optional getAgentCode(final String code) { + return this.codeList.stream().filter(agentCode -> agentCode.getCode().equals(code)).findFirst(); + } + protected List parseFile() { var result = new ArrayList(); @@ -91,14 +103,15 @@ public class CsvAgentCodeService implements AgentCodeService { if (!row.isMapped(ATC_CODE_COLUMN) || !row.isMapped(ATC_NAME_COLUMN)) { throw new FileParsingException("No CSV column for ATC code or name found"); } + final var atcCode = row.get(ATC_CODE_COLUMN).trim(); + if (!AtcCode.isAtcCode(atcCode)) { + logger.warn("Unusable ATC code '{}' - ignored", atcCode); + continue; + } if (row.isMapped(ATC_VERSION_COLUMN)) { - result.add( - new AtcCode( - row.get(ATC_CODE_COLUMN).trim(), - row.get(ATC_NAME_COLUMN), - row.get(ATC_VERSION_COLUMN))); + result.add(new AtcCode(atcCode, row.get(ATC_NAME_COLUMN), row.get(ATC_VERSION_COLUMN))); } else { - result.add(new AtcCode(row.get(ATC_CODE_COLUMN).trim(), row.get(ATC_NAME_COLUMN))); + result.add(new AtcCode(atcCode, row.get(ATC_NAME_COLUMN))); } } return result; diff --git a/src/main/java/dev/dnpm/oshelper/atc/services/OnkostarAgentCodeService.java b/src/main/java/dev/dnpm/oshelper/atc/services/OnkostarAgentCodeService.java index 0dfb1e6..90528aa 100644 --- a/src/main/java/dev/dnpm/oshelper/atc/services/OnkostarAgentCodeService.java +++ b/src/main/java/dev/dnpm/oshelper/atc/services/OnkostarAgentCodeService.java @@ -23,6 +23,7 @@ import dev.dnpm.oshelper.atc.AgentCode; import dev.dnpm.oshelper.atc.AtcCode; import dev.dnpm.oshelper.atc.UnregisteredCode; import java.util.List; +import java.util.Optional; import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; @@ -75,4 +76,40 @@ public class OnkostarAgentCodeService implements AgentCodeService { return new UnregisteredCode(code, shortdesc, synonyms); }); } + + /** + * Queries source for agents with code matching code string. + * + * @param code The code string + * @return An optional agent code if found + */ + @Override + public Optional getAgentCode(final String code) { + var sql = + "SELECT code, shortdesc, synonyms\n" + + " FROM property_catalogue\n" + + " JOIN property_catalogue_version ON (property_catalogue_version.datacatalog_id = property_catalogue.id)\n" + + " JOIN property_catalogue_version_entry p ON (p.property_version_id = property_catalogue_version.id)\n" + + " WHERE name = 'OS.Substanzen'\n" + + " AND LOWER(code) = ?"; + + final var result = + jdbcTemplate.query( + sql, + new Object[] {code}, + (resultSet, i) -> { + var c = resultSet.getString("code"); + var shortdesc = resultSet.getString("shortdesc"); + var synonyms = resultSet.getString("synonyms"); + if (AtcCode.isAtcCode(c)) { + return new AtcCode(c, shortdesc); + } + return new UnregisteredCode(c, shortdesc, synonyms); + }); + + if (result.size() == 1) { + return Optional.of(result.get(0)); + } + return Optional.empty(); + } } diff --git a/src/test/java/dev/dnpm/oshelper/atc/AtcCodeTest.java b/src/test/java/dev/dnpm/oshelper/atc/AtcCodeTest.java index 35fd34e..cab7afd 100644 --- a/src/test/java/dev/dnpm/oshelper/atc/AtcCodeTest.java +++ b/src/test/java/dev/dnpm/oshelper/atc/AtcCodeTest.java @@ -28,8 +28,6 @@ class AtcCodeTest { @Test void testShouldDetectAtcCodeScheme() { - assertTrue(AtcCode.isAtcCode("L01")); - assertTrue(AtcCode.isAtcCode("L01A")); assertTrue(AtcCode.isAtcCode("L01AA")); assertTrue(AtcCode.isAtcCode("L01AA01")); } @@ -43,4 +41,10 @@ class AtcCodeTest { assertFalse(AtcCode.isAtcCode("Z01AA")); assertFalse(AtcCode.isAtcCode("L01AA0")); } + + @Test + void testShouldNotAcceptAtcCodeShorterThanLevel4Or5() { + assertFalse(AtcCode.isAtcCode("L01")); + assertFalse(AtcCode.isAtcCode("L01A")); + } } diff --git a/src/test/java/dev/dnpm/oshelper/atc/CsvAgentCodeServiceTest.java b/src/test/java/dev/dnpm/oshelper/atc/CsvAgentCodeServiceTest.java index 82fd837..3877c9a 100644 --- a/src/test/java/dev/dnpm/oshelper/atc/CsvAgentCodeServiceTest.java +++ b/src/test/java/dev/dnpm/oshelper/atc/CsvAgentCodeServiceTest.java @@ -24,6 +24,8 @@ import static org.assertj.core.api.Assertions.assertThat; import dev.dnpm.oshelper.atc.services.AgentCodeService; import dev.dnpm.oshelper.atc.services.CsvAgentCodeService; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; class CsvAgentCodeServiceTest { @@ -36,4 +38,11 @@ class CsvAgentCodeServiceTest { assertThat(actual).hasSize(1); assertThat(actual.get(0).getCode()).isEqualTo("L01XA01"); } + + @ParameterizedTest + @ValueSource(strings = {"L", "L01", "L01E"}) + void shouldNotReturnIgnoredAtcCodeLevels(String code) { + var actual = agentCodeService.getAgentCode(code); + assertThat(actual).isEmpty(); + } } -- cgit v1.2.3