10 Commits

8 changed files with 95 additions and 49 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "bzkf-rwdp-check" name = "bzkf-rwdp-check"
version = "0.3.0" version = "0.3.3"
edition = "2021" edition = "2021"
authors = ["Paul-Christian Volkmer <volkmer_p@ukw.de>"] authors = ["Paul-Christian Volkmer <volkmer_p@ukw.de>"]
description = "Anwendung zur Durchführung einer Plausibilitätsprüfung anhand der Daten für die BZKF Real World Data Platform." description = "Anwendung zur Durchführung einer Plausibilitätsprüfung anhand der Daten für die BZKF Real World Data Platform."
@ -11,7 +11,7 @@ clap = { version = "4.5", features = ["std", "help", "usage", "derive", "error-c
console = "0.15" console = "0.15"
csv = "1.3" csv = "1.3"
dialoguer = "0.11" dialoguer = "0.11"
itertools = "0.12" itertools = "0.13"
mysql = "25.0" mysql = "25.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
urlencoding = "2.1" urlencoding = "2.1"

View File

@ -22,7 +22,8 @@ flowchart LR
Die Anwendung gibt für die möglichen Quellen der Kennzahlen die Anzahl der _Conditions_, gruppiert nach ICD-10 Gruppen, Die Anwendung gibt für die möglichen Quellen der Kennzahlen die Anzahl der _Conditions_, gruppiert nach ICD-10 Gruppen,
aus. aus.
Unterstützt wird eien OPAL-CSV-Datei (wie für BZKF vorgesehen) und eine Onkostar-Datenbank, basierend auf MariaDB oder MySQL. Unterstützt wird eien OPAL-CSV-Datei (wie für BZKF vorgesehen) und eine Onkostar-Datenbank, basierend auf MariaDB oder
MySQL.
![Ausgabe](docs/screenshot.png) ![Ausgabe](docs/screenshot.png)
@ -39,8 +40,8 @@ Die Anwendung gibt nun eine Liste der ICD-10-Gruppen mit Anzahl der _Conditions_
## Kennzahlen aus der Onkostar-Datenbank ## Kennzahlen aus der Onkostar-Datenbank
Die Anzahl der _Conditions_, gruppiert nach ICD-10-Gruppe, kann auch mit dem Befehl `database` aus der Onkostar-Datenbank Die Anzahl der _Conditions_, gruppiert nach ICD-10-Gruppe, kann auch mit dem Befehl `database` aus der
abgerufen werden. Onkostar-Datenbank abgerufen werden.
``` ```
bzkf-rwdp-check database --user me --year 2024 bzkf-rwdp-check database --user me --year 2024
@ -64,9 +65,12 @@ Der zusätzliche Parameter `--ignore-exports-since` ist optional.
Wird er angegeben, werden keine Einträge mit Exportdatum ab diesem Datum verwendet. Wird er angegeben, werden keine Einträge mit Exportdatum ab diesem Datum verwendet.
Dies eignet sich um nachträglich Zahlen zu einem bestimmten Datum zu ermitteln. Dies eignet sich um nachträglich Zahlen zu einem bestimmten Datum zu ermitteln.
Der Parameter `--include-extern` schließt Meldungen mit externer Diagnosestellung ein. Der optionale Parameter `--include-extern` schließt Meldungen mit externer Diagnosestellung ein.
Diese sind normalerweise nicht enthalten. Diese sind normalerweise nicht enthalten.
Der optionale Parameter `--include-histo-zyto` schließt Meldungen mit Meldeanlass `histologhie_zytologie` ein.
Diese sind normalerweise ebenfalls nicht enthalten.
## Export aus der Onkostar-Datenbank ## Export aus der Onkostar-Datenbank
Die Anwendung ist in der Lage, mit dem Befehl `export` die Spalten Die Anwendung ist in der Lage, mit dem Befehl `export` die Spalten
@ -78,8 +82,9 @@ Die Anwendung ist in der Lage, mit dem Befehl `export` die Spalten
in eine CSV-Datei zum Abgleich mit der OPAL-CSV-Datei zu exportieren. in eine CSV-Datei zum Abgleich mit der OPAL-CSV-Datei zu exportieren.
Hierbei gelten die gleichen Datenbank-Parameter wie unter [Kennzahlen aus der Onkostar-Datenbank](#kennzahlen-aus-der-onkostar-datenbank), Hierbei gelten die gleichen Datenbank-Parameter wie
zusätzlich gibt es noch die folgenden Parameter: unter [Kennzahlen aus der Onkostar-Datenbank](#kennzahlen-aus-der-onkostar-datenbank), zusätzlich gibt es noch die
folgenden Parameter:
``` ```
Options: Options:
@ -92,5 +97,5 @@ Options:
Die Anwendung kann auch die Conditions in der CSV-Datei mit der Onkostar-Datenbank direkt vergleichen. Die Anwendung kann auch die Conditions in der CSV-Datei mit der Onkostar-Datenbank direkt vergleichen.
Hierzu kann der Befehl `compare` genutzt werden. Dieser verwendet alle Optionen für die Datenbank und die Option `--file` Hierzu kann der Befehl `compare` genutzt werden. Dieser verwendet alle Optionen für die Datenbank und die
für die CSV-Datei und gibt eine Übersicht auf der Konsole aus. Option `--file` für die CSV-Datei und gibt eine Übersicht auf der Konsole aus.

View File

@ -20,6 +20,7 @@
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use regex::Regex; use regex::Regex;
use std::path::PathBuf;
#[derive(Parser)] #[derive(Parser)]
#[command(author, version, about)] #[command(author, version, about)]
@ -34,7 +35,7 @@ pub enum SubCommand {
#[command(about = "Ermittelt die Prüfwerte aus einem CSV-File für OPAL")] #[command(about = "Ermittelt die Prüfwerte aus einem CSV-File für OPAL")]
OpalFile { OpalFile {
#[arg(short, long, help = "CSV-File für Opal")] #[arg(short, long, help = "CSV-File für Opal")]
file: String, file: PathBuf,
}, },
#[command(about = "Ermittelt die Prüfwerte aus der Onkostar-Datenbank")] #[command(about = "Ermittelt die Prüfwerte aus der Onkostar-Datenbank")]
Database { Database {
@ -63,6 +64,11 @@ pub enum SubCommand {
ignore_exports_since: Option<String>, ignore_exports_since: Option<String>,
#[arg(long, help = "Meldungen mit externer Diagnose einschließen")] #[arg(long, help = "Meldungen mit externer Diagnose einschließen")]
include_extern: bool, include_extern: bool,
#[arg(
long,
help = "Meldungen mit Meldeanlass 'histologie_zytologie' einschließen"
)]
include_histo_zyto: bool,
}, },
#[command( #[command(
about = "Erstellt eine (reduzierte) CSV-Datei zum direkten Vergleich mit der OPAL-CSV-Datei" about = "Erstellt eine (reduzierte) CSV-Datei zum direkten Vergleich mit der OPAL-CSV-Datei"
@ -90,7 +96,7 @@ pub enum SubCommand {
#[arg(short = 'u', long, help = "Benutzername")] #[arg(short = 'u', long, help = "Benutzername")]
user: String, user: String,
#[arg(short = 'o', long, help = "Ausgabedatei")] #[arg(short = 'o', long, help = "Ausgabedatei")]
output: String, output: PathBuf,
#[arg(short = 'y', long, help = "Jahr der Diagnose")] #[arg(short = 'y', long, help = "Jahr der Diagnose")]
year: String, year: String,
#[arg(long, value_parser = value_is_date, help = "Ignoriere LKR-Exporte seit Datum")] #[arg(long, value_parser = value_is_date, help = "Ignoriere LKR-Exporte seit Datum")]
@ -99,6 +105,11 @@ pub enum SubCommand {
xls_csv: bool, xls_csv: bool,
#[arg(long, help = "Meldungen mit externer Diagnose einschließen")] #[arg(long, help = "Meldungen mit externer Diagnose einschließen")]
include_extern: bool, include_extern: bool,
#[arg(
long,
help = "Meldungen mit Meldeanlass 'histologie_zytologie' einschließen"
)]
include_histo_zyto: bool,
}, },
#[command(about = "Abgleich zwischen CSV-Datei für OPAL und Onkostar-Datenbank")] #[command(about = "Abgleich zwischen CSV-Datei für OPAL und Onkostar-Datenbank")]
Compare { Compare {
@ -124,13 +135,18 @@ pub enum SubCommand {
#[arg(short = 'u', long, help = "Benutzername")] #[arg(short = 'u', long, help = "Benutzername")]
user: String, user: String,
#[arg(short, long, help = "CSV-File für Opal")] #[arg(short, long, help = "CSV-File für Opal")]
file: String, file: PathBuf,
#[arg(short = 'y', long, help = "Jahr der Diagnose")] #[arg(short = 'y', long, help = "Jahr der Diagnose")]
year: String, year: String,
#[arg(long, value_parser = value_is_date, help = "Ignoriere LKR-Exporte seit Datum")] #[arg(long, value_parser = value_is_date, help = "Ignoriere LKR-Exporte seit Datum")]
ignore_exports_since: Option<String>, ignore_exports_since: Option<String>,
#[arg(long, help = "Meldungen mit externer Diagnose einschließen")] #[arg(long, help = "Meldungen mit externer Diagnose einschließen")]
include_extern: bool, include_extern: bool,
#[arg(
long,
help = "Meldungen mit Meldeanlass 'histologie_zytologie' einschließen"
)]
include_histo_zyto: bool,
}, },
} }

View File

@ -63,7 +63,7 @@ impl Check {
icd10_code: Self::map_icd_code(&record.icd10_code), icd10_code: Self::map_icd_code(&record.icd10_code),
}) })
.sorted_by_key(|record| record.icd10_code.to_string()) .sorted_by_key(|record| record.icd10_code.to_string())
.group_by(|record| record.icd10_code.to_string()) .chunk_by(|record| record.icd10_code.to_string())
.into_iter() .into_iter()
.map(|(icd10, group)| (icd10, group.collect::<Vec<_>>())) .map(|(icd10, group)| (icd10, group.collect::<Vec<_>>()))
.map(|record| Icd10GroupSize { .map(|record| Icd10GroupSize {

View File

@ -18,9 +18,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
use std::time::Duration;
use mysql::prelude::Queryable; use mysql::prelude::Queryable;
use mysql::{params, Pool}; use mysql::{params, Pool};
use std::time::Duration;
use crate::common::{ExportData, Icd10GroupSize}; use crate::common::{ExportData, Icd10GroupSize};
use crate::resources::{EXPORT_QUERY, SQL_QUERY}; use crate::resources::{EXPORT_QUERY, SQL_QUERY};
@ -39,13 +40,19 @@ impl DatabaseSource {
year: &str, year: &str,
ignore_exports_since: &str, ignore_exports_since: &str,
include_extern: bool, include_extern: bool,
include_histo_zyto: bool,
) -> Result<Vec<Icd10GroupSize>, ()> { ) -> Result<Vec<Icd10GroupSize>, ()> {
match Pool::new(self.0.as_str()) { match Pool::new(self.0.as_str()) {
Ok(pool) => { Ok(pool) => {
if let Ok(mut connection) = pool.try_get_conn(Duration::from_secs(3)) { if let Ok(mut connection) = pool.try_get_conn(Duration::from_secs(3)) {
return match connection.exec_map( return match connection.exec_map(
SQL_QUERY, SQL_QUERY,
params! {"year" => year, "ignore_exports_since" => ignore_exports_since, "include_extern" => if include_extern { 1 } else { 0 } }, params! {
"year" => year,
"ignore_exports_since" => ignore_exports_since,
"include_extern" => if include_extern { 1 } else { 0 },
"include_histo_zyto" => if include_histo_zyto { 1 } else { 0 }
},
|(icd10_group, count)| Icd10GroupSize { |(icd10_group, count)| Icd10GroupSize {
name: icd10_group, name: icd10_group,
size: count, size: count,
@ -70,13 +77,19 @@ impl DatabaseSource {
ignore_exports_since: &str, ignore_exports_since: &str,
use_pat_id: bool, use_pat_id: bool,
include_extern: bool, include_extern: bool,
include_histo_zyto: bool,
) -> Result<Vec<ExportData>, ()> { ) -> Result<Vec<ExportData>, ()> {
match Pool::new(self.0.as_str()) { match Pool::new(self.0.as_str()) {
Ok(pool) => { Ok(pool) => {
if let Ok(mut connection) = pool.try_get_conn(Duration::from_secs(3)) { if let Ok(mut connection) = pool.try_get_conn(Duration::from_secs(3)) {
return match connection.exec_map( return match connection.exec_map(
EXPORT_QUERY, EXPORT_QUERY,
params! {"year" => year, "ignore_exports_since" => ignore_exports_since, "include_extern" => if include_extern { 1 } else { 0 } }, params! {
"year" => year,
"ignore_exports_since" => ignore_exports_since,
"include_extern" => if include_extern { 1 } else { 0 },
"include_histo_zyto" => if include_histo_zyto { 1 } else { 0 }
},
|(condition_id, icd_10_code, diagnosis_date, pat_id)| ExportData { |(condition_id, icd_10_code, diagnosis_date, pat_id)| ExportData {
condition_id, condition_id,
icd_10_code, icd_10_code,

View File

@ -19,7 +19,6 @@
*/ */
use std::error::Error; use std::error::Error;
use std::path::Path;
use clap::Parser; use clap::Parser;
use console::{style, Term}; use console::{style, Term};
@ -88,10 +87,17 @@ fn print_items(items: &[Icd10GroupSize]) {
} }
fn print_extern_notice(include_extern: bool) { fn print_extern_notice(include_extern: bool) {
let _ = Term::stdout().write_line(format!("{} Die Datenbankanfrage schließt Meldungen mit externer Diagnose {}.", style("Hinweis:").bold().underlined(), match include_extern { let _ = Term::stdout().write_line(
format!(
"{} Die Datenbankanfrage schließt Meldungen mit externer Diagnose {}.",
style("Hinweis:").bold().underlined(),
match include_extern {
true => style("ein").yellow(), true => style("ein").yellow(),
false => style("nicht ein (Standard)").green() false => style("nicht ein (Standard)").green(),
}).as_str()); }
)
.as_str(),
);
} }
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
@ -99,8 +105,8 @@ fn main() -> Result<(), Box<dyn Error>> {
match Cli::parse().cmd { match Cli::parse().cmd {
SubCommand::OpalFile { file } => { SubCommand::OpalFile { file } => {
let items = opal::OpalCsvFile::check(Path::new(&file)) let items =
.map_err(|_e| "Kann Datei nicht lesen")?; opal::OpalCsvFile::check(file.as_path()).map_err(|_e| "Kann Datei nicht lesen")?;
print_items(&items); print_items(&items);
} }
@ -113,6 +119,7 @@ fn main() -> Result<(), Box<dyn Error>> {
year, year,
ignore_exports_since, ignore_exports_since,
include_extern, include_extern,
include_histo_zyto,
} => { } => {
let password = request_password_if_none(password); let password = request_password_if_none(password);
let year = sanitize_year(year); let year = sanitize_year(year);
@ -129,6 +136,7 @@ fn main() -> Result<(), Box<dyn Error>> {
&year, &year,
&ignore_exports_since.unwrap_or("9999-12-31".into()), &ignore_exports_since.unwrap_or("9999-12-31".into()),
include_extern, include_extern,
include_histo_zyto,
) )
.map_err(|_e| "Fehler bei Zugriff auf die Datenbank")?; .map_err(|_e| "Fehler bei Zugriff auf die Datenbank")?;
@ -149,6 +157,7 @@ fn main() -> Result<(), Box<dyn Error>> {
ignore_exports_since, ignore_exports_since,
xls_csv, xls_csv,
include_extern, include_extern,
include_histo_zyto,
} => { } => {
let password = request_password_if_none(password); let password = request_password_if_none(password);
let year = sanitize_year(year); let year = sanitize_year(year);
@ -166,6 +175,7 @@ fn main() -> Result<(), Box<dyn Error>> {
&ignore_exports_since.unwrap_or("9999-12-31".into()), &ignore_exports_since.unwrap_or("9999-12-31".into()),
pat_id, pat_id,
include_extern, include_extern,
include_histo_zyto,
) )
.map_err(|_e| "Fehler bei Zugriff auf die Datenbank")?; .map_err(|_e| "Fehler bei Zugriff auf die Datenbank")?;
@ -177,7 +187,7 @@ fn main() -> Result<(), Box<dyn Error>> {
writer_builder = writer_builder.delimiter(b';'); writer_builder = writer_builder.delimiter(b';');
} }
let mut writer = writer_builder let mut writer = writer_builder
.from_path(Path::new(&output)) .from_path(output.as_path())
.expect("writeable file"); .expect("writeable file");
items items
@ -189,7 +199,7 @@ fn main() -> Result<(), Box<dyn Error>> {
"{} Conditions für das Jahr {} in Datei '{}' exportiert", "{} Conditions für das Jahr {} in Datei '{}' exportiert",
items.len(), items.len(),
year, year,
output output.to_str().unwrap_or_default()
)) ))
.green() .green()
.to_string(), .to_string(),
@ -208,6 +218,7 @@ fn main() -> Result<(), Box<dyn Error>> {
year, year,
ignore_exports_since, ignore_exports_since,
include_extern, include_extern,
include_histo_zyto,
} => { } => {
let password = request_password_if_none(password); let password = request_password_if_none(password);
let year = sanitize_year(year); let year = sanitize_year(year);
@ -225,13 +236,14 @@ fn main() -> Result<(), Box<dyn Error>> {
&ignore_exports_since.unwrap_or("9999-12-31".into()), &ignore_exports_since.unwrap_or("9999-12-31".into()),
pat_id, pat_id,
include_extern, include_extern,
include_histo_zyto,
) )
.map_err(|_e| "Fehler bei Zugriff auf die Datenbank")?; .map_err(|_e| "Fehler bei Zugriff auf die Datenbank")?;
let _ = term.clear_last_lines(1); let _ = term.clear_last_lines(1);
let csv_items = opal::OpalCsvFile::export(Path::new(&file)) let csv_items =
.map_err(|_e| "Kann Datei nicht lesen")?; opal::OpalCsvFile::export(file.as_path()).map_err(|_e| "Kann Datei nicht lesen")?;
let mut not_in_csv = db_items let mut not_in_csv = db_items
.iter() .iter()
@ -250,7 +262,7 @@ fn main() -> Result<(), Box<dyn Error>> {
"{} Conditions aus der Datenbank für das Jahr {} - aber nicht in Datei '{}'", "{} Conditions aus der Datenbank für das Jahr {} - aber nicht in Datei '{}'",
not_in_csv.len(), not_in_csv.len(),
year, year,
file file.to_str().unwrap_or_default()
)) ))
.green() .green()
.to_string(), .to_string(),
@ -308,7 +320,7 @@ fn main() -> Result<(), Box<dyn Error>> {
&style(format!( &style(format!(
"{} Conditions aus Datei '{}' - aber nicht in der Datenbank für das Jahr {}", "{} Conditions aus Datei '{}' - aber nicht in der Datenbank für das Jahr {}",
not_in_db.len(), not_in_db.len(),
file, file.to_str().unwrap_or_default(),
year year
)) ))
.green() .green()

View File

@ -29,23 +29,23 @@ FROM (
EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID') AS pid, EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID') AS pid,
lme.versionsnummer, lme.versionsnummer,
SHA2(CONCAT('https://fhir.diz.uk-erlangen.de/identifiers/onkostar-xml-condition-id|', EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID'), 'condition', EXTRACTVALUE(lme.xml_daten, '//Diagnose/@Tumor_ID')), 256) AS cond_id, SHA2(CONCAT('https://fhir.diz.uk-erlangen.de/identifiers/onkostar-xml-condition-id|', EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID'), 'condition', EXTRACTVALUE(lme.xml_daten, '//Diagnose/@Tumor_ID')), 256) AS cond_id,
SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Primaertumor_ICD_Code'), ' ', 1) AS condcodingcode, SUBSTRING_INDEX(EXTRACTVALUE(lm.xml_daten, '//Primaertumor_ICD_Code'), ' ', 1) AS condcodingcode,
SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1) AS diagnosedatum, SUBSTRING_INDEX(EXTRACTVALUE(lm.xml_daten, '//Diagnosedatum'), ' ', 1) AS diagnosedatum,
SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) AS diagnosejahr SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lm.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) AS diagnosejahr
FROM lkr_meldung_export lme FROM lkr_meldung_export lme
JOIN lkr_meldung lm ON (lm.id = lme.lkr_meldung AND lme.typ <> '-1' AND lm.extern <= :include_extern) JOIN lkr_meldung lm ON (lm.id = lme.lkr_meldung AND lme.typ <> '-1' AND lm.extern <= :include_extern)
JOIN lkr_export le ON (le.id = lme.lkr_export) WHERE lm.xml_daten LIKE '%ICD_Version%'
WHERE lme.xml_daten LIKE '%ICD_Version%' AND SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lm.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) = :year
AND SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) = :year AND (lm.xml_daten LIKE '%<cTNM%' OR lm.xml_daten LIKE '%<pTNM%' OR lm.xml_daten LIKE '%<Menge_Histologie>%' OR lm.xml_daten LIKE '%<Menge_Weitere_Klassifikation>%')
AND (lme.xml_daten LIKE '%<cTNM%' OR lme.xml_daten LIKE '%<pTNM%' OR lme.xml_daten LIKE '%<Menge_Histologie>%' OR lme.xml_daten LIKE '%<Menge_Weitere_Klassifikation>%') AND (lm.xml_daten NOT LIKE '%histologie_zytologie%' OR 1 = :include_histo_zyto)
AND le.exportiert_am < :ignore_exports_since
) o1 ) o1
LEFT OUTER JOIN ( LEFT OUTER JOIN (
SELECT SELECT DISTINCT
SHA2(CONCAT('https://fhir.diz.uk-erlangen.de/identifiers/onkostar-xml-condition-id|', EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID'), 'condition', EXTRACTVALUE(lme.xml_daten, '//Diagnose/@Tumor_ID')), 256) AS cond_id, SHA2(CONCAT('https://fhir.diz.uk-erlangen.de/identifiers/onkostar-xml-condition-id|', EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID'), 'condition', EXTRACTVALUE(lme.xml_daten, '//Diagnose/@Tumor_ID')), 256) AS cond_id,
MAX(versionsnummer) AS max_version CASE WHEN le.exportiert_am < :ignore_exports_since THEN MAX(versionsnummer) ELSE ~0 END AS max_version
FROM lkr_meldung_export lme FROM lkr_meldung_export lme
JOIN lkr_export le ON (lme.lkr_export = le.id)
WHERE SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) = :year WHERE SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) = :year
GROUP BY cond_id ORDER BY cond_id GROUP BY cond_id ORDER BY cond_id

View File

@ -115,22 +115,22 @@ FROM (
EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID') AS pid, EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID') AS pid,
lme.versionsnummer, lme.versionsnummer,
SHA2(CONCAT('https://fhir.diz.uk-erlangen.de/identifiers/onkostar-xml-condition-id|', EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID'), 'condition', EXTRACTVALUE(lme.xml_daten, '//Diagnose/@Tumor_ID')), 256) AS cond_id, SHA2(CONCAT('https://fhir.diz.uk-erlangen.de/identifiers/onkostar-xml-condition-id|', EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID'), 'condition', EXTRACTVALUE(lme.xml_daten, '//Diagnose/@Tumor_ID')), 256) AS cond_id,
SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Primaertumor_ICD_Code'), ' ', 1) AS condcodingcode, SUBSTRING_INDEX(EXTRACTVALUE(lm.xml_daten, '//Primaertumor_ICD_Code'), ' ', 1) AS condcodingcode,
SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) AS diagnosejahr SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lm.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) AS diagnosejahr
FROM lkr_meldung_export lme FROM lkr_meldung_export lme
JOIN lkr_meldung lm ON (lm.id = lme.lkr_meldung AND lme.typ <> '-1' AND lm.extern <= :include_extern) JOIN lkr_meldung lm ON (lm.id = lme.lkr_meldung AND lme.typ <> '-1' AND lm.extern <= :include_extern)
JOIN lkr_export le ON (le.id = lme.lkr_export)
WHERE lme.xml_daten LIKE '%ICD_Version%' WHERE lme.xml_daten LIKE '%ICD_Version%'
AND SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) = :year AND SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lm.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) = :year
AND (lme.xml_daten LIKE '%<cTNM%' OR lme.xml_daten LIKE '%<pTNM%' OR lme.xml_daten LIKE '%<Menge_Histologie>%' OR lme.xml_daten LIKE '%<Menge_Weitere_Klassifikation>%') AND (lm.xml_daten LIKE '%<cTNM%' OR lm.xml_daten LIKE '%<pTNM%' OR lm.xml_daten LIKE '%<Menge_Histologie>%' OR lm.xml_daten LIKE '%<Menge_Weitere_Klassifikation>%')
AND le.exportiert_am < :ignore_exports_since AND (lm.xml_daten NOT LIKE '%histologie_zytologie%' OR 1 = :include_histo_zyto)
) o1 ) o1
LEFT OUTER JOIN ( LEFT OUTER JOIN (
SELECT SELECT DISTINCT
SHA2(CONCAT('https://fhir.diz.uk-erlangen.de/identifiers/onkostar-xml-condition-id|', EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID'), 'condition', EXTRACTVALUE(lme.xml_daten, '//Diagnose/@Tumor_ID')), 256) AS cond_id, SHA2(CONCAT('https://fhir.diz.uk-erlangen.de/identifiers/onkostar-xml-condition-id|', EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID'), 'condition', EXTRACTVALUE(lme.xml_daten, '//Diagnose/@Tumor_ID')), 256) AS cond_id,
MAX(versionsnummer) AS max_version CASE WHEN le.exportiert_am < :ignore_exports_since THEN MAX(versionsnummer) ELSE ~0 END AS max_version
FROM lkr_meldung_export lme FROM lkr_meldung_export lme
JOIN lkr_export le ON (lme.lkr_export = le.id)
WHERE SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) = :year WHERE SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) = :year
GROUP BY cond_id ORDER BY cond_id GROUP BY cond_id ORDER BY cond_id