mirror of
https://github.com/pcvolkmer/onkostar-plugin-dnpm.git
synced 2025-07-03 17:52:53 +00:00
Issue #19: Integration des ATC-Codes-Plugins in dieses Plugin
closes #19
This commit is contained in:
@ -1,15 +0,0 @@
|
||||
package ATCCodes;
|
||||
|
||||
/**
|
||||
* Common Agent Code definition
|
||||
*
|
||||
* @author Paul-Christian Volkmer
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public interface AgentCode extends Comparable<AgentCode> {
|
||||
String getCode();
|
||||
|
||||
String getName();
|
||||
|
||||
CodeSystem getSystem();
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
package ATCCodes;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* ATC-Code as used in WHO XML file
|
||||
*
|
||||
* @author Paul-Christian Volkmer
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class AtcCode implements AgentCode {
|
||||
|
||||
private final String code;
|
||||
private final String name;
|
||||
|
||||
public AtcCode(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public CodeSystem getSystem() {
|
||||
return CodeSystem.ATC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final AgentCode agentCode) {
|
||||
return this.name.toLowerCase().compareTo(agentCode.getName().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
AgentCode otherAgentCode = (AgentCode) o;
|
||||
return Objects.equals(code.toLowerCase(), otherAgentCode.getCode().toLowerCase())
|
||||
&& Objects.equals(name.toLowerCase(), otherAgentCode.getName().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(code.toLowerCase(), name.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft auf gültiges ATCCode-Schema ab Ebene 2
|
||||
* @param code Der zu prüfende Code
|
||||
* @return Gibt <code>true</code> zurück, wenn der angegebene Code dem ATCCode-Schema entspricht
|
||||
*/
|
||||
public static boolean isAtcCode(String code) {
|
||||
return null != code
|
||||
&& ! code.isBlank()
|
||||
&& code.matches("[ABCDGHJLMNPRSV][0-2][1-9]([A-Z]([A-Z]([0-9]{2})?)?)?");
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
package ATCCodes;
|
||||
|
||||
import de.itc.onkostar.api.Disease;
|
||||
import de.itc.onkostar.api.Procedure;
|
||||
import de.itc.onkostar.api.analysis.AnalyzerRequirement;
|
||||
import de.itc.onkostar.api.analysis.IProcedureAnalyzer;
|
||||
import de.itc.onkostar.api.analysis.OnkostarPluginType;
|
||||
import ATCCodes.services.AgentCodeService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Plugin implementation
|
||||
* Provides methods exposed to Onkostar
|
||||
*
|
||||
* @author Paul-Christian Volkmer
|
||||
* @since 0.1.0
|
||||
*/
|
||||
@Component
|
||||
public class AtcCodesPlugin implements IProcedureAnalyzer {
|
||||
|
||||
private final List<AgentCodeService> agentCodeServices;
|
||||
|
||||
public AtcCodesPlugin(List<AgentCodeService> agentCodeServices) {
|
||||
this.agentCodeServices = agentCodeServices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OnkostarPluginType getType() {
|
||||
return OnkostarPluginType.BACKEND_SERVICE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
return "0.4.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ATC-Codes und Substanzen";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "ATC-Codes und Substanzen";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSynchronous() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnalyzerRequirement getRequirement() {
|
||||
return AnalyzerRequirement.PROCEDURE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRelevantForDeletedProcedure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRelevantForAnalyzer(final Procedure procedure, final Disease disease) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void analyze(final Procedure procedure, final Disease disease) {
|
||||
// Nothing to do - should never be called
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list with ATC codes and agents.
|
||||
* Usage in script:
|
||||
*
|
||||
* <pre>
|
||||
* executePluginMethod(
|
||||
* 'AtcCodesPlugin',
|
||||
* 'query',
|
||||
* { q: '', size: 10 },
|
||||
* function (result) {console.log(result);},
|
||||
* false
|
||||
* );
|
||||
* </pre>
|
||||
*
|
||||
* @param input The data Map
|
||||
* @return The result list filtered by input
|
||||
*/
|
||||
public List<AgentCode> query(final Map<String, Object> input) {
|
||||
String query = "";
|
||||
if (null != input.get("q")) {
|
||||
query = input.get("q").toString();
|
||||
}
|
||||
|
||||
int size = Integer.parseInt(input.get("size").toString());
|
||||
if (size == 0) {
|
||||
size = 10;
|
||||
}
|
||||
var result = new ArrayList<AgentCode>();
|
||||
for (var agentCodeService : this.agentCodeServices) {
|
||||
result.addAll(agentCodeService.findAgentCodes(query, size));
|
||||
}
|
||||
return result.stream().distinct().sorted().collect(Collectors.toList());
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
package ATCCodes;
|
||||
|
||||
/**
|
||||
* This enum provides usable coding systems
|
||||
*
|
||||
* @author Paul-Christion Volkmer
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public enum CodeSystem {
|
||||
ATC,
|
||||
UNREGISTERED
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package ATCCodes;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if any file parsing error occurs
|
||||
*
|
||||
* @author Paul-Christian Volkmer
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class FileParsingException extends RuntimeException {
|
||||
|
||||
public FileParsingException(final String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package ATCCodes;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Unregistered code as used in Onkostar database
|
||||
*
|
||||
* @author Paul-Christian Volkmer
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class UnregisteredCode implements AgentCode {
|
||||
|
||||
private final String code;
|
||||
private final String name;
|
||||
|
||||
public UnregisteredCode(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public CodeSystem getSystem() {
|
||||
return CodeSystem.UNREGISTERED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final AgentCode agentCode) {
|
||||
return this.name.toLowerCase().compareTo(agentCode.getName().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
AgentCode otherAgentCode = (AgentCode) o;
|
||||
return Objects.equals(code.toLowerCase(), otherAgentCode.getCode().toLowerCase())
|
||||
&& Objects.equals(name.toLowerCase(), otherAgentCode.getName().toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(code.toLowerCase(), name.toLowerCase());
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package ATCCodes.services;
|
||||
|
||||
import ATCCodes.AgentCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Common interface for agent code services
|
||||
*
|
||||
* @author Paul-Christian Volkmer
|
||||
*/
|
||||
public interface AgentCodeService {
|
||||
|
||||
/**
|
||||
* Queries source for agents with name and code starting with query string.
|
||||
* If size is zero, all available results will be returned.
|
||||
*
|
||||
* @param query The query string
|
||||
* @param size Maximal amount of responses
|
||||
* @return A list with agent codes
|
||||
*/
|
||||
List<AgentCode> findAgentCodes(String query, int size);
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
package ATCCodes.services;
|
||||
|
||||
import ATCCodes.AgentCode;
|
||||
import ATCCodes.AtcCode;
|
||||
import ATCCodes.FileParsingException;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Service to query for agent codes based on WHO xml file
|
||||
*
|
||||
* @author Paul-Christian Volkmer
|
||||
* @since 0.1.0
|
||||
*/
|
||||
@Service
|
||||
public class CsvAtcCodeService extends FileBasedAgentCodeService {
|
||||
|
||||
public CsvAtcCodeService(final ResourceLoader resourceLoader) {
|
||||
super(resourceLoader);
|
||||
}
|
||||
|
||||
protected List<AgentCode> parseFile(final ResourceLoader resourceLoader) {
|
||||
var result = new ArrayList<AgentCode>();
|
||||
var filename = getFilePath("atc.csv");
|
||||
try {
|
||||
var inputStream = resourceLoader.getResource(filename).getInputStream();
|
||||
var parser = CSVFormat.RFC4180
|
||||
.withHeader()
|
||||
.withSkipHeaderRecord()
|
||||
.parse(new InputStreamReader(inputStream));
|
||||
for (var row : parser) {
|
||||
if (!row.isMapped("CODE") || !row.isMapped("NAME")) {
|
||||
throw new FileParsingException("No CSV column 'CODE' or 'NAME' found");
|
||||
}
|
||||
result.add(new AtcCode(row.get("CODE"), row.get("NAME")));
|
||||
}
|
||||
logger.info("Found CSV file for ATC-Codes.");
|
||||
return result;
|
||||
} catch (IOException | FileParsingException e) {
|
||||
logger.warn("Error reading CSV file '{}' for ATC-Codes. Proceeding without data", filename);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
package ATCCodes.services;
|
||||
|
||||
import ATCCodes.AgentCode;
|
||||
import org.apache.commons.lang.SystemUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Abstract {@link AgentCodeService} for use with files that will load information into memory
|
||||
*
|
||||
* @author Paul-Christian Volkmer
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public abstract class FileBasedAgentCodeService implements AgentCodeService {
|
||||
|
||||
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
protected final List<AgentCode> codeList = new ArrayList<>();
|
||||
|
||||
FileBasedAgentCodeService(final ResourceLoader resourceLoader) {
|
||||
this.codeList.addAll(parseFile(resourceLoader));
|
||||
}
|
||||
|
||||
static String getFilePath(final String filename) {
|
||||
String pluginPathPart = "onkostar/files/onkostar/plugins/onkostar-plugin-atccodes";
|
||||
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
return String.format("file:///c:/%s/%s", pluginPathPart, filename);
|
||||
} else if (SystemUtils.IS_OS_LINUX) {
|
||||
return String.format("file:///opt/%s/%s", pluginPathPart, filename);
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
protected abstract List<AgentCode> parseFile(final ResourceLoader resourceLoader);
|
||||
|
||||
/**
|
||||
* Queries source for agents code starting with or name containing query string.
|
||||
* If size is zero, all available results will be returned.
|
||||
*
|
||||
* @param query The query string
|
||||
* @param size Maximal amount of responses
|
||||
* @return A list with agent codes
|
||||
*/
|
||||
@Override
|
||||
public List<AgentCode> findAgentCodes(final String query, final int size) {
|
||||
var resultStream = this.codeList.stream().filter(agentCode ->
|
||||
agentCode.getCode().toLowerCase().startsWith(query.toLowerCase())
|
||||
|| agentCode.getName().toLowerCase().contains(query.toLowerCase())
|
||||
);
|
||||
|
||||
if (size > 0) {
|
||||
return resultStream.limit(size).collect(Collectors.toList());
|
||||
}
|
||||
return resultStream.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
package ATCCodes.services;
|
||||
|
||||
import ATCCodes.AgentCode;
|
||||
import ATCCodes.AtcCode;
|
||||
import ATCCodes.UnregisteredCode;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implementation of {@link AgentCodeService} that uses database to query for unregistered agents
|
||||
*
|
||||
* @author Paul-Christian Volkmer
|
||||
* @since 0.1.0
|
||||
*/
|
||||
@Service
|
||||
public class OnkostarAgentCodeService implements AgentCodeService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
public OnkostarAgentCodeService(final DataSource dataSource) {
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries source for agents code starting with or name containing query string.
|
||||
* If size is zero, all available results will be returned.
|
||||
*
|
||||
* @param query The query string
|
||||
* @param size Maximal amount of responses
|
||||
* @return A list with agent codes
|
||||
*/
|
||||
@Override
|
||||
public List<AgentCode> findAgentCodes(final String query, final int size) {
|
||||
var sql = "SELECT code, shortdesc\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) LIKE ? OR LOWER(shortdesc) LIKE ? OR LOWER(synonyms) LIKE ?)";
|
||||
|
||||
if (size > 0) {
|
||||
sql = sql + " LIMIT " + size;
|
||||
}
|
||||
|
||||
return jdbcTemplate.query(
|
||||
sql,
|
||||
new Object[]{query + "%", "%" + query + "%", "%" + query + "%"},
|
||||
(resultSet, i) -> {
|
||||
var code = resultSet.getString("code");
|
||||
var shortdesc = resultSet.getString("shortdesc");
|
||||
if (AtcCode.isAtcCode(code)) {
|
||||
return new AtcCode(code, shortdesc);
|
||||
}
|
||||
return new UnregisteredCode(code, shortdesc);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
package ATCCodes.services;
|
||||
|
||||
import ATCCodes.AgentCode;
|
||||
import ATCCodes.AtcCode;
|
||||
import ATCCodes.FileParsingException;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Service to query for agent codes based on WHO xml file
|
||||
*
|
||||
* @author Paul-Christian Volkmer
|
||||
* @since 0.1.0
|
||||
*/
|
||||
@Service
|
||||
public class WhoAtcCodeService extends FileBasedAgentCodeService {
|
||||
|
||||
public WhoAtcCodeService(final ResourceLoader resourceLoader) {
|
||||
super(resourceLoader);
|
||||
}
|
||||
|
||||
protected List<AgentCode> parseFile(final ResourceLoader resourceLoader) {
|
||||
var result = new ArrayList<AgentCode>();
|
||||
var filename = getFilePath("atc.xml");
|
||||
try {
|
||||
var inputStream = resourceLoader.getResource(filename).getInputStream();
|
||||
var context = JAXBContext.newInstance(XmlResource.class);
|
||||
var xmlResource = (XmlResource) context.createUnmarshaller().unmarshal(inputStream);
|
||||
for (var row : xmlResource.data.rows) {
|
||||
if (null == row.code || null == row.name) {
|
||||
throw new FileParsingException("No XML attribute 'ATCCode' or 'Name' found");
|
||||
}
|
||||
result.add(new AtcCode(row.code, row.name));
|
||||
}
|
||||
logger.info("Found WHO XML file for ATC-Codes.");
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
logger.warn("Error reading WHO XML file '{}' for ATC-Codes. Proceeding without inserting data", filename);
|
||||
} catch (JAXBException | FileParsingException e) {
|
||||
logger.warn("Error parsing WHO XML file '{}' for ATC-Codes. Proceeding without inserting data", filename);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@XmlRootElement(name = "xml")
|
||||
private static class XmlResource {
|
||||
@XmlElement(name = "data", namespace = "urn:schemas-microsoft-com:rowset")
|
||||
public XmlData data;
|
||||
}
|
||||
|
||||
private static class XmlData {
|
||||
@XmlElement(name = "row", namespace = "#RowsetSchema")
|
||||
public List<XmlRow> rows;
|
||||
}
|
||||
|
||||
private static class XmlRow {
|
||||
@XmlAttribute(name = "ATCCode")
|
||||
public String code;
|
||||
|
||||
@XmlAttribute(name = "Name")
|
||||
public String name;
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
package DNPM.services.systemtherapie;
|
||||
|
||||
import ATCCodes.AtcCode;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import de.itc.onkostar.api.Item;
|
||||
import de.itc.onkostar.api.Procedure;
|
||||
import de.ukw.ccc.onkostar.atccodes.AtcCode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
Reference in New Issue
Block a user