From 200a042e053e5e9b1fe996a4913630da35fc9175 Mon Sep 17 00:00:00 2001 From: Paul-Christian Volkmer Date: Wed, 28 Feb 2024 13:15:44 +0100 Subject: [PATCH] feat: export data from database into OPAL-like CSV file --- README.md | 22 ++++++++++++++++- src/cli.rs | 30 +++++++++++++++++++++++ src/database.rs | 47 ++++++++++++++++++++++++++++++++--- src/main.rs | 47 +++++++++++++++++++++++++++++++++++ src/resources/export.sql | 53 ++++++++++++++++++++++++++++++++++++++++ src/resources/mod.rs | 2 ++ 6 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 src/resources/export.sql diff --git a/README.md b/README.md index b17242c..1419f28 100644 --- a/README.md +++ b/README.md @@ -56,4 +56,24 @@ Options: -p, --password Passwort. Wenn nicht angegeben, wird danach gefragt -u, --user Benutzername -y, --year Jahr der Diagnose -``` \ No newline at end of file +``` + +## Export aus der Onkostar-Datenbank + +Die Anwendung ist in der Lage, die Spalten + +* `pat_id`: Patienten-ID (optional über Parameter `--pat-id`) +* `cond_id`: Condition-ID +* `conditiondate`: Datum der Diagnose +* `condcodingcode`: Der ICD-10-Code der Diagnose + +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), +zusätzlich gibt es noch die folgenden Parameter: + +``` +Options: + --pat-id Export mit Klartext-Patienten-OD + -o, --output Ausgabedatei +``` diff --git a/src/cli.rs b/src/cli.rs index d88a9a4..6e7beab 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -59,4 +59,34 @@ pub enum SubCommand { #[arg(short = 'y', long, help = "Jahr der Diagnose")] year: String, }, + #[command( + about = "Erstellt eine (reduzierte) CSV-Datei zum direkten Vergleich mit der OPAL-CSV-Datei" + )] + Export { + #[arg(long, help = "Export mit Klartext-Patienten-ID")] + pat_id: bool, + #[arg(short = 'D', long, help = "Datenbank-Name", default_value = "onkostar")] + database: String, + #[arg( + short = 'h', + long, + help = "Datenbank-Host", + default_value = "localhost" + )] + host: String, + #[arg(short = 'P', long, help = "Datenbank-Host", default_value = "3306")] + port: u16, + #[arg( + short = 'p', + long, + help = "Passwort. Wenn nicht angegeben, wird danach gefragt" + )] + password: Option, + #[arg(short = 'u', long, help = "Benutzername")] + user: String, + #[arg(short = 'o', long, help = "Ausgabedatei")] + output: String, + #[arg(short = 'y', long, help = "Jahr der Diagnose")] + year: String, + }, } diff --git a/src/database.rs b/src/database.rs index 51850d2..7686b0b 100644 --- a/src/database.rs +++ b/src/database.rs @@ -20,12 +20,25 @@ use mysql::prelude::Queryable; use mysql::{params, Pool}; +use serde::Serialize; use crate::common::Icd10GroupSize; -use crate::resources::SQL_QUERY; +use crate::resources::{EXPORT_QUERY, SQL_QUERY}; pub struct DatabaseSource(String); +#[derive(Serialize, Debug)] +pub struct ExportData { + #[serde(rename = "pat_id")] + pat_id: Option, + #[serde(rename = "cond_id")] + condition_id: String, + #[serde(rename = "condition_date")] + diagnosis_date: String, + #[serde(rename = "condcodingcode")] + icd_10_code: String, +} + impl DatabaseSource { pub fn new(database: &str, host: &str, password: &str, port: u16, user: &str) -> Self { let password = urlencoding::encode(password); @@ -50,8 +63,36 @@ impl DatabaseSource { }; } } - Err(e) => { - println!("{}", e); + Err(_) => { + return Err(()); + } + } + + Err(()) + } + + pub fn export(&self, year: &str, use_pat_id: bool) -> Result, ()> { + match Pool::new(self.0.as_str()) { + Ok(pool) => { + if let Ok(mut connection) = pool.get_conn() { + return match connection.exec_map( + EXPORT_QUERY, + params! {"year" => year}, + |(condition_id, icd_10_code, diagnosis_date, pat_id)| ExportData { + condition_id, + icd_10_code, + diagnosis_date, + pat_id: if use_pat_id { Some(pat_id) } else { None }, + }, + ) { + Ok(result) => Ok(result), + Err(_) => { + return Err(()); + } + }; + } + } + Err(_) => { return Err(()); } } diff --git a/src/main.rs b/src/main.rs index 4366090..1270510 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ use std::path::Path; use clap::Parser; use console::{style, Term}; +use csv::Writer; use crate::cli::{Cli, SubCommand}; use crate::common::Icd10GroupSize; @@ -96,6 +97,52 @@ fn main() -> Result<(), Box> { print_items(&items); } + SubCommand::Export { + pat_id, + database, + host, + password, + port, + user, + output, + year, + } => { + let password = if let Some(password) = password { + password + } else { + let password = dialoguer::Password::new() + .with_prompt("Password") + .interact() + .unwrap_or_default(); + let _ = term.clear_last_lines(1); + password + }; + + let year = if year.len() == 4 { + year + } else { + format!("2{:0>3}", year) + }; + + let _ = term.write_line( + &style(format!("Warte auf Daten für das Diagnosejahr {}...", year)) + .blue() + .to_string(), + ); + + let db = DatabaseSource::new(&database, &host, &password, port, &user); + let items = db + .export(&year, pat_id) + .map_err(|_e| "Fehler bei Zugriff auf die Datenbank")?; + + let _ = term.clear_last_lines(1); + + let mut writer = Writer::from_path(Path::new(&output)).expect("writeable file"); + + items + .iter() + .for_each(|item| writer.serialize(item).unwrap()); + } } Ok(()) diff --git a/src/resources/export.sql b/src/resources/export.sql new file mode 100644 index 0000000..8c358c1 --- /dev/null +++ b/src/resources/export.sql @@ -0,0 +1,53 @@ +/* + * This file is part of bzkf-rwdp-check + * + * Copyright (C) 2024 Comprehensive Cancer Center Mainfranken and contributors. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +SELECT + o1.cond_id, + o1.condcodingcode, + o1.diagnosedatum, + o1.pid +FROM ( + + SELECT DISTINCT + EXTRACTVALUE(lme.xml_daten, '//Patienten_Stammdaten/@Patient_ID') AS pid, + 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, + SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Primaertumor_ICD_Code'), ' ', 1) AS condcodingcode, + SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1) AS diagnosedatum, + SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) AS diagnosejahr + FROM lkr_meldung_export lme + JOIN lkr_meldung lm ON (lm.id = lme.lkr_meldung AND lme.typ <> '-1') + WHERE lme.xml_daten LIKE '%ICD_Version%' + AND SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) = :year + AND (lme.xml_daten LIKE '%%') + + ) o1 + LEFT OUTER JOIN ( + + SELECT + 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 + FROM lkr_meldung_export lme + WHERE SUBSTRING_INDEX(SUBSTRING_INDEX(EXTRACTVALUE(lme.xml_daten, '//Diagnosedatum'), ' ', 1), '.', -1) = :year + GROUP BY cond_id ORDER BY cond_id + + ) o2 +ON (o1.cond_id = o2.cond_id AND o1.versionsnummer < max_version) +WHERE diagnosejahr = :year AND o2.cond_id IS NULL; \ No newline at end of file diff --git a/src/resources/mod.rs b/src/resources/mod.rs index b070577..78c2878 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -19,3 +19,5 @@ */ pub const SQL_QUERY: &str = include_str!("query.sql"); + +pub const EXPORT_QUERY: &str = include_str!("export.sql");