Initial commit
This commit is contained in:
commit
3cba772836
1341 changed files with 532924 additions and 0 deletions
BIN
BIT-C/.RData
Normal file
BIN
BIT-C/.RData
Normal file
Binary file not shown.
1
BIT-C/.Rprofile
Normal file
1
BIT-C/.Rprofile
Normal file
|
|
@ -0,0 +1 @@
|
|||
source("renv/activate.R")
|
||||
13
BIT-C/BIT-C.Rproj
Normal file
13
BIT-C/BIT-C.Rproj
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
Version: 1.0
|
||||
|
||||
RestoreWorkspace: Default
|
||||
SaveWorkspace: Default
|
||||
AlwaysSaveHistory: Default
|
||||
|
||||
EnableCodeIndexing: Yes
|
||||
UseSpacesForTab: Yes
|
||||
NumSpacesForTab: 2
|
||||
Encoding: UTF-8
|
||||
|
||||
RnwWeave: Sweave
|
||||
LaTeX: pdfLaTeX
|
||||
664
BIT-C/app.R
Normal file
664
BIT-C/app.R
Normal file
|
|
@ -0,0 +1,664 @@
|
|||
# Präambel ####
|
||||
|
||||
library(shiny)
|
||||
library(dplyr)
|
||||
library(ggplot2)
|
||||
library(haven)
|
||||
library(officer)
|
||||
library(DBI)
|
||||
library(RSQLite)
|
||||
|
||||
# Infrastruktur ####
|
||||
|
||||
PFAD_DOWNLOAD_SKRIPT = "../API/get_data_bitc.R"
|
||||
PFAD_PSEUDONYM_SKRIPT = "../get_pseudo.R"
|
||||
AKZENT_FARBE = "#8B2635"
|
||||
|
||||
APP_VERZEICHNIS = normalizePath(getwd())
|
||||
|
||||
absPath = function(pfad) {
|
||||
if (grepl("^([A-Za-z]:[/\\\\]|/)", pfad)) return(pfad)
|
||||
file.path(APP_VERZEICHNIS, pfad)
|
||||
}
|
||||
|
||||
PFAD_DOWNLOAD_SKRIPT = normalizePath(absPath(PFAD_DOWNLOAD_SKRIPT), mustWork = FALSE)
|
||||
PFAD_PSEUDONYM_SKRIPT = normalizePath(absPath(PFAD_PSEUDONYM_SKRIPT), mustWork = FALSE)
|
||||
|
||||
BITC_DISCLAIMER = paste0(
|
||||
"Diese Auswertung ist ein Hilfsmittel fuer klinisches Fachpersonal und ersetzt ",
|
||||
"keine klinische Diagnose. Die Interpretation obliegt der behandelnden Person."
|
||||
)
|
||||
|
||||
bitc_items = data.frame(
|
||||
nr = 1:63,
|
||||
bereich = c(
|
||||
rep("Bewältigung bestimmter Probleme und Symptome", 27),
|
||||
rep("Ziele im zwischenmenschlichen Bereich", 16),
|
||||
rep("Verbesserung des Wohlbefindens", 6),
|
||||
rep("Orientierung im Leben", 3),
|
||||
rep("Selbstbezogene Ziele", 11)
|
||||
),
|
||||
stichwort = c(
|
||||
rep("Depressives Erleben", 4),
|
||||
rep("Körperliche Selbstverletzung", 2),
|
||||
rep("Ängste", 4),
|
||||
rep("Zwanghafte Gedanken und Handlungen", 2),
|
||||
rep("Traumatische Erlebnisse", 1),
|
||||
rep("Suchtverhalten (bezogen auf Alkohol, illegale Drogen oder Medikamente)", 4),
|
||||
rep("Essverhalten", 2),
|
||||
rep("Schlaf", 1),
|
||||
rep("Sexualität", 1),
|
||||
rep("Körperliche Schmerzen und Krankheiten", 2),
|
||||
rep("Schwierigkeiten in bestimmten Lebensbereichen", 3),
|
||||
rep("Stress", 1),
|
||||
rep("Bestehende Partnerschaft", 3),
|
||||
rep("Elternschaft und aktuelle Familie", 3),
|
||||
rep("Herkunftsfamilie", 1),
|
||||
rep("Andere Beziehungen", 2),
|
||||
rep("Alleinsein und Trauer", 2),
|
||||
rep("Selbstbehauptung und Abgrenzung", 2),
|
||||
rep("Kontakt und Nähe", 3),
|
||||
rep("Bewegung und Aktivität", 2),
|
||||
rep("Entspannung und Gelassenheit", 2),
|
||||
rep("Wohlbefinden", 2),
|
||||
rep("Vergangenheit, Gegenwart und Zukunft", 3),
|
||||
rep("Einstellung zu mir selbst", 2),
|
||||
rep("Bedürfnisse und Wünsche", 3),
|
||||
rep("Verantwortung, Leistung und Kontrolle", 4),
|
||||
rep("Umgang mit Gefühlen", 2)
|
||||
),
|
||||
text = c(
|
||||
"negative, kreisende Gedanken oder Schuldgefühle überwinden.",
|
||||
"aus meiner gedrückten Stimmung, Traurigkeit oder innerer Leere herauskommen.",
|
||||
"mit Stimmungsschwankungen besser umgehen lernen.",
|
||||
"wieder mehr Antrieb und Energie bekommen.",
|
||||
"lernen, mir keine körperlichen Verletzungen mehr zuzufügen (z.B. mich absichtlich schneiden oder brennen).",
|
||||
"Selbstmordgedanken zu überwinden und wieder Lebenswillen finden.",
|
||||
"eine konkrete Angst bewältigen oder besser mit ihr umgehen lernen.",
|
||||
"lernen, Angst- und Panikfällen in den Griff zu bekommen.",
|
||||
"lernen, ohne Angst und unsicheres Verhalten (z.B. Erröten, Stottern) unter die Leute zu gehen.",
|
||||
"lernen wieder Dinge zu tun, die ich jetzt aus Angst vermeide.",
|
||||
"ständig wiederkehrende, quälende Gedanken oder Impulse besser kontrollieren lernen.",
|
||||
"wiederholte, sinnlose und zeitraubende Handlungen (übertriebenes Händewaschen, Ordnen, Prüfen, Zählen etc.) einschränken lernen.",
|
||||
"traumatische Erlebnisse verarbeiten (z.B. schwerer Unfall, Gewaltverbrechen, Vergewaltigung, Naturkatastrophe u.a.).",
|
||||
"den körperlichen Entzug von Suchtmitteln durchführen.",
|
||||
"ohne Suchtmittel leben lernen.",
|
||||
"meinen Suchtmittelkonsum kontrollieren lernen.",
|
||||
"mit schwierigen Situationen anders umgehen lernen, statt zu Suchtmittel zu greifen.",
|
||||
"meine Essprobleme (Magersucht, Ess-Brechsucht, Esssucht etc.) bewältigen.",
|
||||
"mit meinem Übergewicht umgehen lernen (es reduzieren oder so akzeptieren).",
|
||||
"meine Schlafprobleme (Schwierigkeiten beim Ein- oder Durchschlafen, frühes Erwachen etc.) bewältigen.",
|
||||
"sexuelle Probleme bewältigen.",
|
||||
"mit körperlichen Schmerzen umgehen lernen.",
|
||||
"mit meiner körperlichen Krankheit umgehen lernen.",
|
||||
"meine Wohnsituation klären.",
|
||||
"konkrete Probleme im Zusammenhang mit meiner Arbeit oder meiner Ausbildung bewältigen.",
|
||||
"meinen Alltag besser organisieren lernen.",
|
||||
"besser mit Stresssituationen umgehen lernen.",
|
||||
"die Beziehung mit meinem Partner / meiner Partnerin verbessern.",
|
||||
"das Sexualleben mit meinem Partner / meiner Partnerin verbessern.",
|
||||
"meine Erwartungen und Gefühle bzgl. Partner / Partnerin klären.",
|
||||
"mich mit meiner Vater- bzw. Mutterrolle auseinandersetzen.",
|
||||
"die Beziehung zu meinem Kind / meinen Kindern verbessern.",
|
||||
"versuchen, etwas an der ganzen familiären Situation zu verändern.",
|
||||
"die Beziehung zu meinen Eltern verändern (mich ablösen, Schuldgefühle oder Abhängigkeit überwinden etc.).",
|
||||
"die Beziehung zu bestimmten Personen aus dem privaten oder beruflichen Umfeld klären oder verbessern.",
|
||||
"die Trennung von meinem Ex-Partner / meiner Ex-Partnerin besser verarbeiten.",
|
||||
"meine Zeit allein verbringen lernen.",
|
||||
"den Tod einer geliebten Person besser verarbeiten.",
|
||||
"mich anderen gegenüber besser durchsetzen und abgrenzen lernen.",
|
||||
"mit den Reaktionen anderer (Kritik, Ablehnung, Lob etc.) auf mein Verhalten besser umgehen lernen.",
|
||||
"lernen, besser mit Menschen Kontakt aufzunehmen und zu pflegen (z.B. lernen, wie ich Leute kennenlerne, Freundschaften aufbaue etc.).",
|
||||
"Nähe zulassen und Vertrauen zu anderen Menschen aufbauen lernen.",
|
||||
"mich auf eine neue Partnerschaft vorbereiten.",
|
||||
"mehr Sport und andere körperliche Aktivitäten betreiben.",
|
||||
"meine Freizeit aktiver gestalten (Hobbies, kulturelle Aktivitäten etc.).",
|
||||
"Techniken erlernen, die mir helfen, mich zu entspannen.",
|
||||
"lernen, Probleme und Herausforderungen gelassener anzugehen.",
|
||||
"mehr Optimismus und Lebensfreude entwickeln.",
|
||||
"lernen, mich in meinem Körper wohl zu fühlen.",
|
||||
"mit Teilen meiner Vergangenheit besser zurechtkommen.",
|
||||
"mir klarer werden, wer ich bin, was ich kann und was ich will.",
|
||||
"neue Zukunftsperspektiven (private oder berufliche) erarbeiten.",
|
||||
"mehr Selbstvertrauen und Selbstsicherheit entwickeln.",
|
||||
"mich akzeptieren lernen so wie ich bin.",
|
||||
"meine Bedürfnisse wahrnehmen und ausdrücken lernen.",
|
||||
"meine Grenzen besser erkennen und danach handeln lernen.",
|
||||
"eigene Wünsche und Pläne besser verwirklichen lernen.",
|
||||
"lernen, Verantwortung für mich selbst zu übernehmen (eigenständig leben, Entscheidungen treffen usw.).",
|
||||
"mehr Selbstdisziplin und Durchhaltevermögen entwickeln.",
|
||||
"meine hohen Ansprüche an mich oder an andere herabsetzen lernen.",
|
||||
"Verantwortung und Kontrolle abgeben lernen.",
|
||||
"Gefühle zulassen und äußern lernen.",
|
||||
"mit starken negativen Gefühlen (z.B. Ärger, Wutausbrüchen) umgehen lernen."
|
||||
),
|
||||
stringsAsFactors = FALSE
|
||||
)
|
||||
|
||||
# Helper ####
|
||||
|
||||
item_angekreuzt = function(x) {
|
||||
if (!length(x)) return(FALSE)
|
||||
x = suppressWarnings(as.numeric(x[[1]]))
|
||||
isTRUE(!is.na(x) && x != 0)
|
||||
}
|
||||
|
||||
item_volltext = function(nr, text) {
|
||||
paste0(nr, ". Mit Hilfe der Psychotherapie möchte ich ", text)
|
||||
}
|
||||
|
||||
lookup_item = function(nr) {
|
||||
treffer = bitc_items[bitc_items$nr == nr, , drop = FALSE]
|
||||
if (nrow(treffer) == 0) return(NULL)
|
||||
treffer[1, ]
|
||||
}
|
||||
|
||||
prio_item_aufloesen = function(ziel_nr, zusatzziel_texte) {
|
||||
if (is.na(ziel_nr)) return(NULL)
|
||||
ziel_nr = as.integer(ziel_nr)
|
||||
if (ziel_nr >= 1 && ziel_nr <= 63) {
|
||||
item = lookup_item(ziel_nr)
|
||||
if (is.null(item)) {
|
||||
return(list(nr = ziel_nr, text = NULL, warnung = paste0("Ziel Nr. ", ziel_nr, " konnte keinem bekannten Item zugeordnet werden.")))
|
||||
}
|
||||
return(list(nr = ziel_nr, text = item_volltext(item$nr, item$text), warnung = NULL))
|
||||
}
|
||||
if (ziel_nr >= 64 && ziel_nr <= 67) {
|
||||
idx = ziel_nr - 63
|
||||
freitext = zusatzziel_texte[[idx]]
|
||||
if (is.null(freitext) || !nzchar(trimws(freitext))) {
|
||||
return(list(nr = ziel_nr, text = paste0("(Eigenes Ziel ", idx, " – kein Text eingetragen)"), warnung = NULL))
|
||||
}
|
||||
return(list(nr = ziel_nr, text = freitext, warnung = NULL))
|
||||
}
|
||||
list(nr = ziel_nr, text = NULL, warnung = paste0("Ziel Nr. ", ziel_nr, " liegt außerhalb des gültigen Bereichs (1–67)."))
|
||||
}
|
||||
|
||||
render_alle_angekreuzten = function(angekreuzte_nrs, ausgefuellte_zusatzziele) {
|
||||
if (length(angekreuzte_nrs) == 0 && length(ausgefuellte_zusatzziele) == 0) {
|
||||
return(div(class = "item-zeile", "Keine angekreuzten Ziele gefunden."))
|
||||
}
|
||||
|
||||
bereiche_geordnet = unique(bitc_items$bereich)
|
||||
|
||||
bereich_tags = lapply(bereiche_geordnet, function(bereich) {
|
||||
items_b = bitc_items[bitc_items$bereich == bereich & bitc_items$nr %in% angekreuzte_nrs, , drop = FALSE]
|
||||
if (nrow(items_b) == 0) return(NULL)
|
||||
|
||||
stichworte_geordnet = unique(items_b$stichwort)
|
||||
sw_tags = lapply(stichworte_geordnet, function(sw) {
|
||||
items_sw = items_b[items_b$stichwort == sw, , drop = FALSE]
|
||||
item_divs = lapply(seq_len(nrow(items_sw)), function(i) {
|
||||
it = items_sw[i, ]
|
||||
div(class = "item-zeile",
|
||||
span(class = "item-nr", paste0(it$nr, ".")),
|
||||
span(class = "item-text",
|
||||
paste0("Mit Hilfe der Psychotherapie möchte ich ", it$text))
|
||||
)
|
||||
})
|
||||
tagList(
|
||||
div(style = "font-weight:600; font-style:italic; margin-top:10px; margin-bottom:4px; color:#555;", sw),
|
||||
tagList(item_divs)
|
||||
)
|
||||
})
|
||||
tagList(
|
||||
h4(style = "margin-top:16px; margin-bottom:4px;", bereich),
|
||||
tagList(sw_tags)
|
||||
)
|
||||
})
|
||||
|
||||
bereich_tags = Filter(Negate(is.null), bereich_tags)
|
||||
|
||||
zusatz_tags = if (length(ausgefuellte_zusatzziele) > 0) {
|
||||
z_divs = lapply(ausgefuellte_zusatzziele, function(z) {
|
||||
div(class = "item-zeile",
|
||||
span(class = "item-nr", paste0(z$nr, ".")),
|
||||
span(class = "item-text", z$text)
|
||||
)
|
||||
})
|
||||
tagList(h4(style = "margin-top:16px;", "Eigene Therapieziele"), tagList(z_divs))
|
||||
} else NULL
|
||||
|
||||
tagList(bereich_tags, zusatz_tags)
|
||||
}
|
||||
|
||||
# UI ####
|
||||
|
||||
app_css = "
|
||||
body { font-family: 'Segoe UI', Arial, sans-serif; background: #f5f5f5; }
|
||||
.app-header {
|
||||
background: #8B2635; color: white; padding: 18px 24px 14px;
|
||||
margin-bottom: 20px; border-radius: 0 0 6px 6px;
|
||||
}
|
||||
.app-header h2 { margin: 0; font-size: 1.5rem; font-weight: 600; }
|
||||
.app-header p { margin: 4px 0 0; opacity: 0.85; font-size: 0.9rem; }
|
||||
.input-panel {
|
||||
background: white; border-radius: 6px; padding: 16px 20px;
|
||||
margin-bottom: 16px; box-shadow: 0 1px 3px rgba(0,0,0,.12);
|
||||
display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap;
|
||||
}
|
||||
.input-panel .form-group { margin-bottom: 0; }
|
||||
.input-panel label { font-weight: 600; color: #333; }
|
||||
.alert-fehler {
|
||||
background: #FFEBEE; border-left: 5px solid #C62828;
|
||||
padding: 12px 16px; margin-bottom: 16px;
|
||||
border-radius: 0 4px 4px 0; color: #7a0000;
|
||||
}
|
||||
.alert-warnung {
|
||||
background: #FFF8E1; border-left: 5px solid #F9A825;
|
||||
padding: 12px 16px; margin-bottom: 16px;
|
||||
border-radius: 0 4px 4px 0; color: #6d3600;
|
||||
}
|
||||
.abschnitt-karte {
|
||||
background: #fff; border: 1px solid #e0e0e0; border-radius: 6px;
|
||||
padding: 16px; margin-bottom: 14px; box-shadow: 0 1px 3px rgba(0,0,0,.06);
|
||||
}
|
||||
.abschnitt-titel {
|
||||
font-size: 1.05em; font-weight: bold; color: #8B2635;
|
||||
margin-bottom: 10px; border-bottom: 2px solid #8B2635; padding-bottom: 6px;
|
||||
}
|
||||
.btn-laden {
|
||||
background: #8B2635 !important; color: white !important;
|
||||
border: none !important; border-radius: 4px !important;
|
||||
padding: 8px 20px !important; font-weight: 600 !important; cursor: pointer;
|
||||
}
|
||||
.btn-laden:hover { background: #6d1e29 !important; }
|
||||
.prio-originaltext { color: #555; font-size: 0.93em; margin-top: 6px; }
|
||||
.item-zeile {
|
||||
display: flex; align-items: baseline; padding: 5px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
.item-zeile:last-child { border-bottom: none; }
|
||||
.item-nr { font-weight: 600; min-width: 36px; color: #555; flex-shrink: 0; }
|
||||
.item-text { flex: 1; }
|
||||
"
|
||||
app_css = gsub("#8B2635", AKZENT_FARBE, app_css, fixed = TRUE)
|
||||
|
||||
ui = fluidPage(
|
||||
tags$head(tags$style(HTML(app_css))),
|
||||
div(class = "app-header",
|
||||
tags$h2("BIT-C — Berner Inventar für Therapieziele"),
|
||||
tags$p("Revidierte Version | Therapieziele und Priorisierung")
|
||||
),
|
||||
div(class = "container-fluid",
|
||||
div(class = "input-panel",
|
||||
div(style = "min-width: 360px; white-space: nowrap;",
|
||||
textInput("pseudonym",
|
||||
label = tagList(
|
||||
"Pseudonym",
|
||||
tags$span(style = "font-weight: normal; font-style: italic; font-size: 0.78em; color: #888; margin-left: 4px; white-space: nowrap;",
|
||||
"optional, hat Vorrang vor Chiffre")
|
||||
),
|
||||
placeholder = "optional", width = "340px")
|
||||
),
|
||||
div(style = "min-width: 180px;",
|
||||
textInput("chiffre", label = "Patientenchiffre",
|
||||
placeholder = "z.B. P000123", width = "180px")
|
||||
),
|
||||
actionButton("btn_suchen", "Auswerten", class = "btn-laden"),
|
||||
div(style = "margin-left: auto;",
|
||||
downloadButton("download_word", "Word-Export (.docx)",
|
||||
style = paste0(
|
||||
"background:", AKZENT_FARBE, "; color:white; border:none;",
|
||||
" font-weight:600; padding:8px 20px; border-radius:4px;"
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
div(class = "ergebnis-container",
|
||||
uiOutput("ergebnis_ui")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# Word-Export ####
|
||||
|
||||
erstelle_bitc_docx = function(erg) {
|
||||
doc = read_docx()
|
||||
|
||||
stil_titel = fp_text(color = AKZENT_FARBE, bold = TRUE, font.size = 18)
|
||||
stil_h2 = fp_text(bold = TRUE, font.size = 13)
|
||||
stil_h3 = fp_text(bold = TRUE, font.size = 11, italic = TRUE)
|
||||
stil_fett = fp_text(bold = TRUE, font.size = 11)
|
||||
stil_kursiv = fp_text(italic = TRUE, font.size = 11)
|
||||
stil_normal = fp_text(font.size = 11)
|
||||
stil_disclaimer = fp_text(font.size = 9, color = "#666666", italic = TRUE)
|
||||
|
||||
doc = body_add_fpar(doc, fpar(ftext("BIT-C — Therapieziele", prop = stil_titel)))
|
||||
doc = body_add_fpar(doc, fpar(ftext(paste0("Patientenchiffre: ", erg$chiffre), prop = stil_normal)))
|
||||
doc = body_add_fpar(doc, fpar(ftext(paste0("Ausfülldatum: ", erg$ausfuelldatum_anzeige), prop = stil_normal)))
|
||||
doc = body_add_fpar(doc, fpar(ftext("", prop = stil_normal)))
|
||||
|
||||
doc = body_add_fpar(doc, fpar(ftext("Priorisierte Ziele", prop = stil_h2)))
|
||||
|
||||
for (z in erg$prio_ziele) {
|
||||
slot_label = paste0("Ziel ", z$slot)
|
||||
if (!is.null(z$warnung)) {
|
||||
doc = body_add_fpar(doc, fpar(ftext(paste0(slot_label, ": "), prop = stil_fett),
|
||||
ftext(z$warnung, prop = stil_kursiv)))
|
||||
} else {
|
||||
formulierung = if (!is.null(z$eigene_formulierung) && nzchar(trimws(z$eigene_formulierung)))
|
||||
z$eigene_formulierung else "(keine eigene Formulierung angegeben)"
|
||||
doc = body_add_fpar(doc, fpar(ftext(paste0(slot_label, ": "), prop = stil_fett),
|
||||
ftext(formulierung, prop = stil_fett)))
|
||||
doc = body_add_fpar(doc, fpar(ftext(z$text, prop = stil_normal)))
|
||||
}
|
||||
doc = body_add_fpar(doc, fpar(ftext("", prop = stil_normal)))
|
||||
}
|
||||
|
||||
doc = body_add_fpar(doc, fpar(ftext("Alle angekreuzten Therapieziele (Übersicht)", prop = stil_h2)))
|
||||
|
||||
bereiche_geordnet = unique(bitc_items$bereich)
|
||||
for (bereich in bereiche_geordnet) {
|
||||
items_b = bitc_items[bitc_items$bereich == bereich & bitc_items$nr %in% erg$angekreuzte_item_nrs, , drop = FALSE]
|
||||
if (nrow(items_b) == 0) next
|
||||
doc = body_add_fpar(doc, fpar(ftext(bereich, prop = stil_h2)))
|
||||
stichworte = unique(items_b$stichwort)
|
||||
for (sw in stichworte) {
|
||||
items_sw = items_b[items_b$stichwort == sw, , drop = FALSE]
|
||||
doc = body_add_fpar(doc, fpar(ftext(sw, prop = stil_h3)))
|
||||
for (i in seq_len(nrow(items_sw))) {
|
||||
it = items_sw[i, ]
|
||||
doc = body_add_fpar(doc, fpar(ftext(item_volltext(it$nr, it$text), prop = stil_normal)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (length(erg$ausgefuellte_zusatzziele) > 0) {
|
||||
doc = body_add_fpar(doc, fpar(ftext("Eigene Therapieziele", prop = stil_h2)))
|
||||
for (z in erg$ausgefuellte_zusatzziele) {
|
||||
doc = body_add_fpar(doc, fpar(ftext(z$text, prop = stil_normal)))
|
||||
}
|
||||
}
|
||||
|
||||
doc = body_add_fpar(doc, fpar(ftext("", prop = stil_normal)))
|
||||
doc = body_add_fpar(doc, fpar(ftext(BITC_DISCLAIMER, prop = stil_disclaimer)))
|
||||
|
||||
doc
|
||||
}
|
||||
|
||||
# Server ####
|
||||
|
||||
server = function(input, output, session) {
|
||||
# --- pseudonym-support-injection v1 ---
|
||||
observe({
|
||||
query = parseQueryString(session$clientData$url_search)
|
||||
if (!is.null(query$pseudonym) && nchar(trimws(query$pseudonym)) > 0) {
|
||||
updateTextInput(session, "pseudonym", value = trimws(query$pseudonym))
|
||||
}
|
||||
})
|
||||
|
||||
observe({
|
||||
query = parseQueryString(session$clientData$url_search)
|
||||
if (!is.null(query$chiffre) && nchar(trimws(query$chiffre)) > 0) {
|
||||
updateTextInput(session, "chiffre", value = toupper(trimws(query$chiffre)))
|
||||
}
|
||||
})
|
||||
|
||||
erg = eventReactive(input$btn_suchen, {
|
||||
chiffre = toupper(trimws(input$chiffre))
|
||||
|
||||
if (!(nchar(trimws(input$pseudonym)) > 0 || grepl("^[A-Z][0-9]{6}$", chiffre))) {
|
||||
return(list(typ = "format_fehler", chiffre = chiffre))
|
||||
}
|
||||
|
||||
if (!file.exists(PFAD_DOWNLOAD_SKRIPT)) {
|
||||
return(list(typ = "datei_fehlt", pfad = PFAD_DOWNLOAD_SKRIPT))
|
||||
}
|
||||
if (!file.exists(PFAD_PSEUDONYM_SKRIPT)) {
|
||||
return(list(typ = "datei_fehlt", pfad = PFAD_PSEUDONYM_SKRIPT))
|
||||
}
|
||||
|
||||
ok_dl = tryCatch({
|
||||
source(PFAD_DOWNLOAD_SKRIPT, local = FALSE)
|
||||
list(ok = TRUE)
|
||||
}, error = function(e) list(ok = FALSE, msg = e$message))
|
||||
if (!ok_dl$ok) return(list(typ = "skript_fehler", meldung = ok_dl$msg))
|
||||
|
||||
db_ordner = local({
|
||||
ordner = dirname(normalizePath(PFAD_PSEUDONYM_SKRIPT, mustWork = FALSE))
|
||||
gefunden = NULL
|
||||
for (i in 1:5) {
|
||||
if (file.exists(file.path(ordner, "pseudonyme.db"))) { gefunden = ordner; break }
|
||||
elternteil = dirname(ordner)
|
||||
if (elternteil == ordner) break
|
||||
ordner = elternteil
|
||||
}
|
||||
gefunden
|
||||
})
|
||||
|
||||
if (is.null(db_ordner)) {
|
||||
return(list(typ = "datei_fehlt",
|
||||
pfad = "pseudonyme.db (nicht gefunden; bis 5 Ebenen oberhalb des Pseudonym-Skripts gesucht)"))
|
||||
}
|
||||
|
||||
alter_wd = getwd()
|
||||
on.exit(setwd(alter_wd), add = TRUE)
|
||||
setwd(db_ordner)
|
||||
|
||||
ok_ps = tryCatch({
|
||||
source(PFAD_PSEUDONYM_SKRIPT, local = FALSE)
|
||||
if (nchar(trimws(input$pseudonym)) > 0) {
|
||||
.pw_wert = trimws(input$pseudonym)
|
||||
.pw_tab = get("pseudo", envir = .GlobalEnv)
|
||||
.pw_treffer = .pw_tab[.pw_tab$pseudonym == .pw_wert, ]
|
||||
if (nrow(.pw_treffer) > 0) chiffre = toupper(trimws(.pw_treffer$chiffre[1]))
|
||||
}
|
||||
list(ok = TRUE)
|
||||
}, error = function(e) list(ok = FALSE, msg = e$message))
|
||||
if (!ok_ps$ok) return(list(typ = "skript_fehler", meldung = ok_ps$msg))
|
||||
|
||||
if (!exists("daten_bitc", envir = .GlobalEnv) || !exists("pseudo", envir = .GlobalEnv)) {
|
||||
return(list(typ = "skript_fehler",
|
||||
meldung = "Objekte 'daten_bitc' oder 'pseudo' nach dem Sourcen nicht vorhanden."))
|
||||
}
|
||||
|
||||
daten_bitc = get("daten_bitc", envir = .GlobalEnv)
|
||||
pseudo = get("pseudo", envir = .GlobalEnv)
|
||||
|
||||
treffer_pseudo = pseudo[pseudo$chiffre == chiffre, , drop = FALSE]
|
||||
if (nrow(treffer_pseudo) == 0) {
|
||||
return(list(typ = "chiffre_nicht_gefunden", chiffre = chiffre))
|
||||
}
|
||||
session_id = treffer_pseudo$pseudonym[1]
|
||||
if (nchar(trimws(input$pseudonym)) > 0) session_id = trimws(input$pseudonym)
|
||||
|
||||
# formr-Exporte verwenden typischerweise 'session' als Session-ID-Spalte
|
||||
if (!"session" %in% names(daten_bitc)) {
|
||||
return(list(typ = "skript_fehler",
|
||||
meldung = "Spalte 'session' nicht in daten_bitc gefunden. Spaltenstruktur des Exports prüfen."))
|
||||
}
|
||||
|
||||
treffer_daten = daten_bitc[daten_bitc$session == session_id, , drop = FALSE]
|
||||
if (nrow(treffer_daten) == 0) {
|
||||
return(list(typ = "keine_daten", chiffre = chiffre))
|
||||
}
|
||||
|
||||
warnung_mehrfach = NULL
|
||||
if (nrow(treffer_daten) > 1) {
|
||||
if ("created" %in% names(treffer_daten)) {
|
||||
treffer_daten = treffer_daten[order(treffer_daten$created, decreasing = TRUE), , drop = FALSE]
|
||||
datum_str = tryCatch(format(as.POSIXct(treffer_daten$created[1]), "%d.%m.%Y %H:%M"), error = function(e) as.character(treffer_daten$created[1]))
|
||||
}
|
||||
warnung_mehrfach = paste0(
|
||||
"Achtung: Für diese Chiffre wurden ", nrow(treffer_daten),
|
||||
" Ausfüllungen gefunden. Es wird die neueste verwendet (", datum_str, ")."
|
||||
)
|
||||
}
|
||||
|
||||
zeile = treffer_daten[1, , drop = FALSE]
|
||||
|
||||
# Ausfülldatum ermitteln
|
||||
# Fallback auf Sys.Date() falls 'created' fehlt oder nicht parsebar
|
||||
ausfuelldatum_anzeige = "unbekannt"
|
||||
ausfuelldatum_fn = format(Sys.Date(), "%Y%m%d")
|
||||
if ("created" %in% names(zeile)) {
|
||||
parsed = tryCatch(as.POSIXct(zeile$created[1]), error = function(e) NULL)
|
||||
if (!is.null(parsed) && !is.na(parsed)) {
|
||||
ausfuelldatum_anzeige = format(parsed, "%d.%m.%Y")
|
||||
ausfuelldatum_fn = format(parsed, "%Y%m%d")
|
||||
}
|
||||
}
|
||||
|
||||
# Angekreuzte Items ermitteln (item1 bis item63)
|
||||
angekreuzte_item_nrs = integer(0)
|
||||
for (i in 1:63) {
|
||||
spalte = paste0("item", i)
|
||||
if (spalte %in% names(zeile)) {
|
||||
if (item_angekreuzt(zeile[[spalte]])) {
|
||||
angekreuzte_item_nrs = c(angekreuzte_item_nrs, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Ausgefüllte Zusatzziele ermitteln (Nummern 64-67)
|
||||
ausgefuellte_zusatzziele = list()
|
||||
for (i in 1:4) {
|
||||
spalte = paste0("zusatzziel_text_", i)
|
||||
if (spalte %in% names(zeile)) {
|
||||
txt = trimws(as.character(zeile[[spalte]][[1]]))
|
||||
if (!is.na(txt) && nzchar(txt) && txt != "NA") {
|
||||
ausgefuellte_zusatzziele = c(ausgefuellte_zusatzziele, list(list(nr = 63L + i, text = txt)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Zusatzziel-Texte als benannter Vektor für die Auflösung
|
||||
zusatzziel_texte = lapply(1:4, function(i) {
|
||||
spalte = paste0("zusatzziel_text_", i)
|
||||
if (spalte %in% names(zeile)) {
|
||||
txt = trimws(as.character(zeile[[spalte]][[1]]))
|
||||
if (!is.na(txt) && nzchar(txt) && txt != "NA") return(txt)
|
||||
}
|
||||
NULL
|
||||
})
|
||||
|
||||
# Priorisierte Ziele aufbauen
|
||||
prio_ziele = list()
|
||||
warnung_ziele = character(0)
|
||||
for (slot in 1:5) {
|
||||
nr_spalte = paste0("ziel_nr_", slot)
|
||||
prio_spalte = paste0("ziel_prio_", slot)
|
||||
ziel_nr = if (nr_spalte %in% names(zeile)) zeile[[nr_spalte]][[1]] else NA_integer_
|
||||
if (is.na(ziel_nr)) next
|
||||
aufgeloest = prio_item_aufloesen(ziel_nr, zusatzziel_texte)
|
||||
if (is.null(aufgeloest)) next
|
||||
eigene_formulierung = ""
|
||||
if (prio_spalte %in% names(zeile)) {
|
||||
val = zeile[[prio_spalte]][[1]]
|
||||
if (!is.na(val)) eigene_formulierung = as.character(val)
|
||||
}
|
||||
eintrag = list(
|
||||
slot = slot,
|
||||
nr = aufgeloest$nr,
|
||||
text = aufgeloest$text,
|
||||
eigene_formulierung = eigene_formulierung,
|
||||
warnung = aufgeloest$warnung
|
||||
)
|
||||
if (!is.null(aufgeloest$warnung)) {
|
||||
warnung_ziele = c(warnung_ziele, aufgeloest$warnung)
|
||||
}
|
||||
prio_ziele = c(prio_ziele, list(eintrag))
|
||||
}
|
||||
|
||||
list(
|
||||
typ = "erfolg",
|
||||
chiffre = chiffre,
|
||||
ausfuelldatum_anzeige = ausfuelldatum_anzeige,
|
||||
ausfuelldatum_fn = ausfuelldatum_fn,
|
||||
warnung_mehrfach = warnung_mehrfach,
|
||||
warnung_ziele = warnung_ziele,
|
||||
prio_ziele = prio_ziele,
|
||||
angekreuzte_item_nrs = angekreuzte_item_nrs,
|
||||
ausgefuellte_zusatzziele = ausgefuellte_zusatzziele
|
||||
)
|
||||
})
|
||||
|
||||
output$ergebnis_ui = renderUI({
|
||||
req(input$btn_suchen)
|
||||
e = erg()
|
||||
|
||||
if (e$typ == "format_fehler") {
|
||||
return(div(class = "alert-fehler",
|
||||
"Die Chiffre entspricht nicht dem erwarteten Format (Buchstabe gefolgt von 6 Ziffern, z.B. P000123)."))
|
||||
}
|
||||
|
||||
if (e$typ == "datei_fehlt") {
|
||||
return(div(class = "alert-fehler",
|
||||
paste0("Skript-Datei nicht gefunden: ", e$pfad)))
|
||||
}
|
||||
|
||||
if (e$typ == "skript_fehler") {
|
||||
return(div(class = "alert-fehler",
|
||||
paste0("Fehler beim Ausführen eines Skripts: ", e$meldung)))
|
||||
}
|
||||
|
||||
if (e$typ == "chiffre_nicht_gefunden") {
|
||||
return(div(class = "alert-fehler",
|
||||
"Keine Pseudonymisierungs-Zuordnung für diese Chiffre gefunden."))
|
||||
}
|
||||
|
||||
if (e$typ == "keine_daten") {
|
||||
return(div(class = "alert-fehler",
|
||||
"Für diese Person liegen keine BIT-C-Daten vor."))
|
||||
}
|
||||
|
||||
warnungen = tagList(
|
||||
if (!is.null(e$warnung_mehrfach)) div(class = "alert-warnung", e$warnung_mehrfach),
|
||||
if (length(e$warnung_ziele) > 0)
|
||||
div(class = "alert-warnung", lapply(e$warnung_ziele, function(w) div(w)))
|
||||
)
|
||||
|
||||
prio_cards = tagList(lapply(e$prio_ziele, function(z) {
|
||||
titel = paste0("Ziel ", z$slot)
|
||||
inhalt = if (!is.null(z$warnung)) {
|
||||
div(class = "alert-warnung", style = "margin-top:6px;", z$warnung)
|
||||
} else {
|
||||
tagList(
|
||||
div(style = "font-weight:600;",
|
||||
if (nzchar(trimws(z$eigene_formulierung)))
|
||||
z$eigene_formulierung
|
||||
else
|
||||
em(style = "font-weight:normal;", "(keine eigene Formulierung angegeben)")
|
||||
),
|
||||
div(class = "prio-originaltext", z$text)
|
||||
)
|
||||
}
|
||||
div(class = "abschnitt-karte",
|
||||
div(class = "abschnitt-titel", titel),
|
||||
inhalt
|
||||
)
|
||||
}))
|
||||
|
||||
uebersicht = div(class = "abschnitt-karte",
|
||||
div(class = "abschnitt-titel", "Alle angekreuzten Therapieziele (Übersicht)"),
|
||||
render_alle_angekreuzten(e$angekreuzte_item_nrs, e$ausgefuellte_zusatzziele)
|
||||
)
|
||||
|
||||
tagList(
|
||||
warnungen,
|
||||
h3(style = "margin-top:8px;", "Priorisierte Ziele"),
|
||||
prio_cards,
|
||||
uebersicht
|
||||
)
|
||||
})
|
||||
|
||||
output$download_word = downloadHandler(
|
||||
filename = function() {
|
||||
e = tryCatch(erg(), error = function(x) NULL)
|
||||
if (is.null(e) || e$typ != "erfolg") return("BITC_export.docx")
|
||||
chiffre_esc = gsub("[^A-Za-z0-9]", "_", e$chiffre)
|
||||
paste0("BITC_", chiffre_esc, "_", e$ausfuelldatum_fn, ".docx")
|
||||
},
|
||||
content = function(file) {
|
||||
e = tryCatch(erg(), error = function(x) NULL)
|
||||
if (is.null(e) || e$typ != "erfolg") return(invisible(NULL))
|
||||
doc = erstelle_bitc_docx(e)
|
||||
print(doc, target = file)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
# Start ####
|
||||
|
||||
shinyApp(ui, server)
|
||||
3019
BIT-C/renv.lock
Normal file
3019
BIT-C/renv.lock
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue