1272 lines
51 KiB
R
1272 lines
51 KiB
R
# Präambel ####
|
||
|
||
PFAD_DOWNLOAD_SKRIPT = "../API/get_data_acqbsqmi.R"
|
||
PFAD_PSEUDONYM_SKRIPT = "../get_pseudo.R"
|
||
AKZENT_FARBE = "#8B2635"
|
||
|
||
ACQBSQMI_DISCLAIMER = paste0(
|
||
"Diese Auswertung ist ein Hilfsmittel fuer klinisches Fachpersonal und ersetzt ",
|
||
"keine klinische Diagnose. Die Interpretation obliegt der behandelnden Person. ",
|
||
"Die dargestellten Referenzwerte sind deskriptive Vergleichswerte aus publizierten ",
|
||
"Stichproben, keine diagnostischen Cutoffs."
|
||
)
|
||
|
||
# OFFEN (1): Keine publizierten diagnostischen Cutoffs fuer ACQ/BSQ/MI in diesem
|
||
# Manual-Auszug. Die App zeigt ausschliesslich deskriptive Einordnung gegenueber
|
||
# den vier Referenzgruppen (Perzentil und Stanine), keine kategoriale Klassifikation
|
||
# ("auffaellig"/"unauffaellig").
|
||
|
||
# OFFEN (2): Diskrepanz N=206 (Tab. A5) vs. N=208 (Tab. A6) bei Kontrollgruppe MIA.
|
||
# Vermutlich OCR-Lesefehler; wirkt sich nicht auf Werte aus. Sichtbar als Fussnote.
|
||
|
||
library(shiny)
|
||
library(dplyr)
|
||
library(ggplot2)
|
||
library(haven)
|
||
library(officer)
|
||
library(DBI)
|
||
library(RSQLite)
|
||
|
||
|
||
# Infrastruktur ####
|
||
|
||
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)
|
||
|
||
|
||
# Helper ####
|
||
|
||
get_label_text = function(original_col, wert) {
|
||
if (is.null(wert) || length(wert) == 0 || is.na(wert[1])) return(NA_character_)
|
||
lbl_attr = attr(original_col, "labels")
|
||
if (!is.null(lbl_attr) && length(lbl_attr) > 0) {
|
||
pos = which(as.vector(lbl_attr) == suppressWarnings(as.numeric(wert[1])))
|
||
if (length(pos) > 0) return(gsub("\\*\\*", "", names(lbl_attr)[pos[1]]))
|
||
}
|
||
NA_character_
|
||
}
|
||
|
||
get_numeric_wert = function(wert) {
|
||
if (is.null(wert) || length(wert) == 0 || is.na(wert[1])) return(NA_real_)
|
||
suppressWarnings(as.numeric(wert[1]))
|
||
}
|
||
|
||
clean_label = function(text) {
|
||
if (is.null(text) || length(text) == 0 || is.na(text[1])) return(NA_character_)
|
||
gsub("\\*\\*", "", trimws(as.character(text[1])))
|
||
}
|
||
|
||
raw_to_char_or_null = function(x) {
|
||
if (is.null(x) || length(x) == 0) return(NULL)
|
||
s = trimws(as.character(x[1]))
|
||
if (is.na(s) || s == "" || s == "NA") NULL else s
|
||
}
|
||
|
||
mi_ist_zutreffend = function(original_col, wert) {
|
||
txt = get_label_text(original_col, wert)
|
||
if (is.na(txt)) return(FALSE)
|
||
grepl("^ja$", trimws(txt), ignore.case = TRUE)
|
||
}
|
||
|
||
berechne_mittelwert_mit_warnung = function(werte, max_fehlend) {
|
||
n_gesamt = length(werte)
|
||
n_fehlend = sum(is.na(werte))
|
||
n_vorhanden = n_gesamt - n_fehlend
|
||
score = if (n_vorhanden == 0) NA_real_ else mean(werte, na.rm = TRUE)
|
||
warnung = if (!is.na(score) && n_fehlend > max_fehlend)
|
||
paste0("Zu viele fehlende Items (", n_fehlend, " von ", n_gesamt,
|
||
" fehlen; Toleranz: max. ", max_fehlend, "). ",
|
||
"Wert ist moeglicherweise eine Ueberschaetzung, mit Vorsicht interpretieren.")
|
||
else NULL
|
||
list(score = score, n_fehlend = n_fehlend, n_gesamt = n_gesamt,
|
||
n_vorhanden = n_vorhanden, warnung = warnung)
|
||
}
|
||
|
||
berechne_perzentil = function(score, skala, gruppe) {
|
||
if (is.null(score) || is.na(score)) return(NA_character_)
|
||
tab = perzentile_acq_bsq_mi[
|
||
perzentile_acq_bsq_mi$skala == skala &
|
||
perzentile_acq_bsq_mi$gruppe == gruppe, ]
|
||
if (nrow(tab) == 0) return(NA_character_)
|
||
tab = tab[order(tab$perzentil), ]
|
||
if (score <= tab$wert[1]) return(paste0("< ", tab$perzentil[1], ". Pz."))
|
||
if (score >= tab$wert[nrow(tab)]) return(paste0("> ", tab$perzentil[nrow(tab)], ". Pz."))
|
||
for (i in seq_len(nrow(tab) - 1)) {
|
||
if (score >= tab$wert[i] && score < tab$wert[i + 1]) {
|
||
p_unten = tab$perzentil[i]; p_oben = tab$perzentil[i + 1]
|
||
w_unten = tab$wert[i]; w_oben = tab$wert[i + 1]
|
||
if (w_oben == w_unten) return(paste0(p_unten, ". Pz."))
|
||
p_interp = p_unten + (score - w_unten) / (w_oben - w_unten) * (p_oben - p_unten)
|
||
return(paste0(round(p_interp), ". Pz."))
|
||
}
|
||
}
|
||
NA_character_
|
||
}
|
||
|
||
berechne_stanine = function(score, skala, gruppe) {
|
||
if (is.null(score) || is.na(score)) return(NA_character_)
|
||
tab = stanine_acq_bsq_mi[
|
||
stanine_acq_bsq_mi$skala == skala &
|
||
stanine_acq_bsq_mi$gruppe == gruppe, ]
|
||
if (nrow(tab) == 0) return(NA_character_)
|
||
for (i in seq_len(nrow(tab))) {
|
||
if (score >= tab$untergrenze[i] && score <= tab$obergrenze[i])
|
||
return(as.character(tab$stanine[i]))
|
||
}
|
||
NA_character_
|
||
}
|
||
|
||
make_vergleich_plot = function(score, skala_key, titel) {
|
||
ref = referenz_acq_bsq_mi[referenz_acq_bsq_mi$skala == skala_key, ]
|
||
kurz_map = c(
|
||
"Paniksyndrom" = "Panik",
|
||
"Andere Angststoerungen" = "And. Angst",
|
||
"Psychosomat. Patienten ohne Angststoerung" = "Psychosomat.",
|
||
"Kontrollpersonen ohne psychische Stoerung" = "Kontrolle"
|
||
)
|
||
ref$gruppe_kurz = kurz_map[ref$gruppe]
|
||
ref$gruppe_f = factor(ref$gruppe_kurz,
|
||
levels = rev(c("Panik", "And. Angst", "Psychosomat.", "Kontrolle")))
|
||
|
||
p = ggplot(ref, aes(y = gruppe_f, x = mittelwert)) +
|
||
geom_errorbar(aes(xmin = mittelwert - sd, xmax = mittelwert + sd),
|
||
orientation = "y", width = 0.3, color = "#9E9E9E", linewidth = 0.8) +
|
||
geom_point(size = 3.5, color = "#757575") +
|
||
scale_x_continuous(limits = c(1, 5), breaks = 1:5) +
|
||
labs(x = "Mittelwert (+-1 SD)", y = NULL,
|
||
title = paste0("Einordnung: ", titel)) +
|
||
theme_minimal(base_size = 11) +
|
||
coord_cartesian(clip = "off") +
|
||
theme(
|
||
plot.title = element_text(size = 10, color = "#555", face = "plain"),
|
||
panel.grid.minor = element_blank(),
|
||
axis.text.y = element_text(size = 10),
|
||
plot.margin = margin(4, 10, 22, 4)
|
||
)
|
||
|
||
if (!is.null(score) && !is.na(score)) {
|
||
p = p +
|
||
geom_vline(xintercept = score, color = AKZENT_FARBE,
|
||
linewidth = 1.3, linetype = "solid") +
|
||
annotate("label", x = score, y = 0.3,
|
||
label = paste0("Pat: ", round(score, 2)),
|
||
color = "white", fill = AKZENT_FARBE,
|
||
size = 3, fontface = "bold", linewidth = 0)
|
||
}
|
||
p
|
||
}
|
||
|
||
normen_tabelle_html = function(score, skala_key) {
|
||
gruppen = c(
|
||
"Paniksyndrom",
|
||
"Andere Angststoerungen",
|
||
"Psychosomat. Patienten ohne Angststoerung",
|
||
"Kontrollpersonen ohne psychische Stoerung"
|
||
)
|
||
kurz_map = c(
|
||
"Paniksyndrom" = "Panik",
|
||
"Andere Angststoerungen" = "And. Angst",
|
||
"Psychosomat. Patienten ohne Angststoerung" = "Psychosomat.",
|
||
"Kontrollpersonen ohne psychische Stoerung" = "Kontrolle"
|
||
)
|
||
reihen = lapply(gruppen, function(g) {
|
||
p_txt = berechne_perzentil(score, skala_key, g)
|
||
s_txt = berechne_stanine(score, skala_key, g)
|
||
tags$tr(
|
||
tags$td(kurz_map[g],
|
||
style = "padding:2px 8px; color:#555; font-size:0.87em;"),
|
||
tags$td(if (is.na(p_txt)) "-" else p_txt,
|
||
style = "padding:2px 8px; font-size:0.87em; text-align:right;"),
|
||
tags$td(if (is.na(s_txt)) "-" else s_txt,
|
||
style = "padding:2px 8px; font-size:0.87em; text-align:right;")
|
||
)
|
||
})
|
||
tags$table(style = "width:100%; border-collapse:collapse; margin-top:4px;",
|
||
tags$thead(tags$tr(
|
||
tags$th("Gruppe", style = "text-align:left; padding:2px 8px; font-size:0.8em; color:#888; font-weight:500;"),
|
||
tags$th("Perzentil", style = "text-align:right; padding:2px 8px; font-size:0.8em; color:#888; font-weight:500;"),
|
||
tags$th("Stanine", style = "text-align:right; padding:2px 8px; font-size:0.8em; color:#888; font-weight:500;")
|
||
)),
|
||
tags$tbody(reihen)
|
||
)
|
||
}
|
||
|
||
score_block_ui = function(score_info, skala_key, titel, plot_id, einheit = "Mittelwert (1-5)") {
|
||
score_text = if (is.null(score_info$score) || is.na(score_info$score))
|
||
"k. A." else sprintf("%.2f", score_info$score)
|
||
tagList(
|
||
if (!is.null(score_info$warnung))
|
||
div(class = "alert-warnung", score_info$warnung),
|
||
div(class = "abschnitt-titel", style = "font-size:1rem; margin-bottom:6px;", titel),
|
||
div(class = "score-zahl", score_text),
|
||
div(einheit, style = "color:#555; font-size:0.85em; margin-bottom:4px;"),
|
||
normen_tabelle_html(score_info$score, skala_key),
|
||
plotOutput(plot_id, height = "155px")
|
||
)
|
||
}
|
||
|
||
acq_bsq_badge_css = function(wert_num) {
|
||
if (is.na(wert_num)) return("background:#E0E0E0; color:#555;")
|
||
switch(as.character(as.integer(round(wert_num))),
|
||
"1" = "background:#4CAF50; color:white;",
|
||
"2" = "background:#F48FB1; color:#333;",
|
||
"3" = "background:#EF5350; color:white;",
|
||
"4" = "background:#B71C1C; color:white;",
|
||
"5" = "background:#4A0000; color:white;",
|
||
"background:#E0E0E0; color:#555;"
|
||
)
|
||
}
|
||
|
||
render_acq_bsq_item = function(item) {
|
||
badge_css = acq_bsq_badge_css(item$wert_num)
|
||
badge_text = if (is.na(item$wert_text)) "k. A." else item$wert_text
|
||
item_text = if (is.na(item$text)) paste0("Item ", item$nr) else item$text
|
||
|
||
div(class = "item-zeile",
|
||
div(class = "item-nr", item$nr_label),
|
||
div(class = "item-text", item_text),
|
||
tags$span(
|
||
style = paste0("border-radius:4px; padding:2px 9px; font-weight:700; ",
|
||
"font-size:0.82em; white-space:nowrap; flex-shrink:0; ",
|
||
badge_css),
|
||
badge_text
|
||
)
|
||
)
|
||
}
|
||
|
||
mi_wert_badge = function(wert_num, label) {
|
||
bg = if (is.na(wert_num)) "#E0E0E0" else
|
||
c("1"="#4CAF50","1.5"="#8BC34A","2"="#CDDC39","2.5"="#FFEB3B",
|
||
"3"="#FFC107","3.5"="#FF9800","4"="#EF5350","4.5"="#D32F2F","5"="#B71C1C")[
|
||
as.character(wert_num)]
|
||
if (is.na(bg) || is.null(bg)) bg = "#9E9E9E"
|
||
col = if (is.na(wert_num) || wert_num < 3) "#333" else "white"
|
||
wert_txt = if (is.na(wert_num)) "k.A." else sprintf("%.1f", wert_num)
|
||
tags$span(
|
||
style = paste0("border-radius:4px; padding:2px 7px; font-weight:700; ",
|
||
"font-size:0.82em; background:", bg, "; color:", col, ";"),
|
||
paste0(label, " ", wert_txt)
|
||
)
|
||
}
|
||
|
||
render_mi_item = function(item) {
|
||
item_text = if (is.na(item$text)) paste0("Situation ", item$nr) else item$text
|
||
|
||
if (!item$zutreffend) {
|
||
return(div(class = "item-zeile",
|
||
div(class = "item-nr", item$nr_label),
|
||
div(class = "item-text", style = "color:#9E9E9E;", item_text),
|
||
tags$span(style = "color:#BDBDBD; font-size:0.82em; flex-shrink:0;",
|
||
"betrifft mich nicht")
|
||
))
|
||
}
|
||
div(class = "item-zeile",
|
||
div(class = "item-nr", item$nr_label),
|
||
div(class = "item-text", item_text),
|
||
div(style = "flex-shrink:0; display:flex; gap:5px;",
|
||
mi_wert_badge(item$begl_num, "Begl"),
|
||
mi_wert_badge(item$allein_num, "Allein")
|
||
)
|
||
)
|
||
}
|
||
|
||
top3_liste_html = function(txt) {
|
||
if (is.null(txt) || is.na(txt) || trimws(txt) == "") return(NULL)
|
||
parts = trimws(strsplit(txt, "[,;\n\r]")[[1]])
|
||
parts = parts[nchar(parts) > 0]
|
||
if (length(parts) == 0) return(NULL)
|
||
tags$ul(style = "margin:4px 0 0 16px; padding:0; font-size:0.92em; color:#444;",
|
||
lapply(parts, tags$li))
|
||
}
|
||
|
||
render_rang_acq_bsq = function(items, n_score, kurzlabels = NULL, weitere_text = NULL) {
|
||
alle_items = items[seq_len(min(n_score + 1L, length(items)))]
|
||
sortiert = alle_items[order(sapply(alle_items, function(x)
|
||
if (is.na(x$wert_num)) Inf else -x$wert_num
|
||
))]
|
||
|
||
make_badge_txt = function(item) {
|
||
if (!is.null(kurzlabels) && !is.na(item$wert_num)) {
|
||
idx = as.integer(round(item$wert_num))
|
||
if (idx >= 1L && idx <= length(kurzlabels)) return(kurzlabels[idx])
|
||
}
|
||
if (is.na(item$wert_text)) "k.A." else item$wert_text
|
||
}
|
||
|
||
rang_nr = 0L
|
||
lapply(sortiert, function(item) {
|
||
is_w = !item$in_score
|
||
if (!is_w) rang_nr <<- rang_nr + 1L
|
||
nr_label = if (is_w) "zus." else paste0(rang_nr, ".")
|
||
item_txt = if (is_w) {
|
||
if (!is.null(weitere_text)) paste0("Weitere: ", weitere_text)
|
||
else if (!is.na(item$text)) item$text else "Weitere"
|
||
} else {
|
||
if (is.na(item$text)) paste0("Item ", item$nr) else item$text
|
||
}
|
||
badge_css = acq_bsq_badge_css(item$wert_num)
|
||
div(class = "item-zeile", style = if (is_w) "background:#F5F5F5;" else "",
|
||
div(class = "item-nr", nr_label),
|
||
div(class = "item-text",
|
||
style = if (is_w) "font-style:italic; font-weight:bold; color:#666;" else "",
|
||
if (!is_w)
|
||
tags$span(style = "color:#aaa; font-size:0.82em; margin-right:5px;",
|
||
paste0("(", item$nr, ")")),
|
||
item_txt
|
||
),
|
||
if (!is.na(item$wert_num))
|
||
tags$span(style = paste0("border-radius:4px; padding:2px 9px; font-weight:700; ",
|
||
"font-size:0.82em; white-space:nowrap; flex-shrink:0; ",
|
||
badge_css),
|
||
make_badge_txt(item))
|
||
)
|
||
})
|
||
}
|
||
|
||
render_rang_mi = function(mi_items) {
|
||
score_items = Filter(function(x) x$in_score, mi_items)
|
||
if (length(score_items) == 0)
|
||
return(div(style = "color:#888; font-size:0.9em;", "Keine Daten."))
|
||
sortiert = score_items[order(sapply(score_items, function(x)
|
||
if (!x$zutreffend || is.na(x$allein_num)) Inf else -x$allein_num
|
||
))]
|
||
delta_style = function(d) {
|
||
if (is.na(d)) return("color:#aaa;")
|
||
if (d >= 1.5) return("color:#B71C1C; font-weight:700;")
|
||
if (d >= 0.5) return("color:#E65100; font-weight:600;")
|
||
"color:#555;"
|
||
}
|
||
badge = function(wert, css) {
|
||
txt = if (is.na(wert)) "–" else sprintf("%.1f", wert)
|
||
tags$span(style = paste0("border-radius:3px; padding:1px 6px; font-weight:700; ",
|
||
"font-size:0.82em; ", css), txt)
|
||
}
|
||
reihen = lapply(sortiert, function(item) {
|
||
sit_txt = if (is.na(item$text)) paste0("Situation ", item$nr) else item$text
|
||
if (!item$zutreffend) {
|
||
return(tags$tr(style = "background:#F5F5F5;",
|
||
tags$td(paste0(item$nr, ". ", sit_txt),
|
||
style = "padding:3px 8px; font-size:0.87em; color:#BDBDBD; font-style:italic;"),
|
||
tags$td("–", style = "padding:3px 8px; text-align:center; color:#BDBDBD; font-size:0.87em;"),
|
||
tags$td("–", style = "padding:3px 8px; text-align:center; color:#BDBDBD; font-size:0.87em;"),
|
||
tags$td("–", style = "padding:3px 8px; text-align:center; color:#BDBDBD; font-size:0.87em;")
|
||
))
|
||
}
|
||
delta = if (!is.na(item$allein_num) && !is.na(item$begl_num))
|
||
item$allein_num - item$begl_num else NA_real_
|
||
tags$tr(
|
||
tags$td(paste0(item$nr, ". ", sit_txt),
|
||
style = "padding:3px 8px; font-size:0.87em; color:#333;"),
|
||
tags$td(badge(item$allein_num, acq_bsq_badge_css(item$allein_num)),
|
||
style = "padding:3px 8px; text-align:center;"),
|
||
tags$td(badge(item$begl_num, acq_bsq_badge_css(item$begl_num)),
|
||
style = "padding:3px 8px; text-align:center;"),
|
||
tags$td(if (is.na(delta)) "–" else sprintf("%+.1f", delta),
|
||
style = paste0("padding:3px 8px; font-size:0.88em; text-align:center; ",
|
||
delta_style(delta)))
|
||
)
|
||
})
|
||
tags$table(style = "width:100%; border-collapse:collapse;",
|
||
tags$thead(tags$tr(
|
||
tags$th("Situation",
|
||
style = "text-align:left; padding:3px 8px; font-size:0.8em; color:#888; border-bottom:1px solid #eee;"),
|
||
tags$th("Allein",
|
||
style = "text-align:center; padding:3px 8px; font-size:0.8em; color:#888; border-bottom:1px solid #eee;"),
|
||
tags$th("Begl.",
|
||
style = "text-align:center; padding:3px 8px; font-size:0.8em; color:#888; border-bottom:1px solid #eee;"),
|
||
tags$th("Δ (A-B)",
|
||
style = "text-align:center; padding:3px 8px; font-size:0.8em; color:#888; border-bottom:1px solid #eee;")
|
||
)),
|
||
tags$tbody(reihen)
|
||
)
|
||
}
|
||
|
||
normen_text_word = function(score, skala_key) {
|
||
gruppen = c(
|
||
"Paniksyndrom",
|
||
"Andere Angststoerungen",
|
||
"Psychosomat. Patienten ohne Angststoerung",
|
||
"Kontrollpersonen ohne psychische Stoerung"
|
||
)
|
||
paste(sapply(gruppen, function(g) {
|
||
p_txt = berechne_perzentil(score, skala_key, g)
|
||
s_txt = berechne_stanine(score, skala_key, g)
|
||
paste0(" ", g, ": Pz. ",
|
||
if (is.na(p_txt)) "-" else p_txt,
|
||
" | Stanine ", if (is.na(s_txt)) "-" else s_txt)
|
||
}), collapse = "\n")
|
||
}
|
||
|
||
|
||
# Datenaufbereitung ####
|
||
|
||
MI_SITUATIONEN = c(
|
||
"Kinos oder Theater",
|
||
"Supermaerkte",
|
||
"Schul- oder Ausbildungsraeume",
|
||
"Kaufhaeuser",
|
||
"Gaststaetten",
|
||
"Museen",
|
||
"Fahrstuehle",
|
||
"Saele oder Stadien",
|
||
"Parkhaeuser oder -garagen",
|
||
"Hohe Plaetze",
|
||
"Geschlossene Raeume (z. B. Tunnel)",
|
||
"Offene Plaetze - aussen (z. B. Strassen, Hoefe)",
|
||
"Offene Plaetze - innen (z. B. grosse Raeume, Hallen)",
|
||
"Fahren mit Bussen",
|
||
"Fahren mit Zuegen",
|
||
"Fahren mit Untergrundbahnen",
|
||
"Fahren mit Flugzeugen",
|
||
"Fahren mit Schiffen",
|
||
"Fahren mit Autos - ueberall",
|
||
"Fahren mit Autos - auf Autobahnen/Landstrassen",
|
||
"Schlange stehen",
|
||
"Bruecken ueberqueren",
|
||
"Parties, Feste oder Zusammenkuenfte",
|
||
"Auf der Strasse gehen",
|
||
"Zu Hause allein sein",
|
||
"Weit weg von zu Hause sein",
|
||
"Menschenmengen",
|
||
"Andere"
|
||
)
|
||
|
||
referenz_acq_bsq_mi = data.frame(
|
||
skala = c("BSQ","BSQ","BSQ","BSQ",
|
||
"ACQ_gesamt","ACQ_gesamt","ACQ_gesamt","ACQ_gesamt",
|
||
"ACQ_koerperliche_krise","ACQ_koerperliche_krise","ACQ_koerperliche_krise","ACQ_koerperliche_krise",
|
||
"ACQ_kontrollverlust","ACQ_kontrollverlust","ACQ_kontrollverlust","ACQ_kontrollverlust",
|
||
"MIA","MIA","MIA","MIA",
|
||
"MIB","MIB","MIB","MIB"),
|
||
gruppe = rep(c("Paniksyndrom","Andere Angststoerungen",
|
||
"Psychosomat. Patienten ohne Angststoerung",
|
||
"Kontrollpersonen ohne psychische Stoerung"), times = 6),
|
||
n = c(356,135,127,208,
|
||
355,139,132,208,
|
||
295,139,132,208,
|
||
295,139,132,208,
|
||
291,73,55,206,
|
||
229,72,54,207),
|
||
mittelwert = c(2.60,2.05,1.66,1.65,
|
||
2.02,1.64,1.31,1.32,
|
||
2.11,1.33,1.24,1.12,
|
||
1.95,1.91,1.38,1.49,
|
||
2.70,1.69,1.43,1.45,
|
||
1.97,1.40,1.29,1.22),
|
||
sd = c(0.72,0.65,0.64,0.51,
|
||
0.61,0.48,0.33,0.32,
|
||
0.85,0.43,0.31,0.26,
|
||
0.71,0.75,0.47,0.50,
|
||
1.04,0.69,0.65,0.48,
|
||
0.84,0.49,0.50,0.35),
|
||
median = c(2.59,2.00,1.53,1.59,
|
||
1.93,1.57,1.21,1.29,
|
||
2.00,1.20,1.00,1.00,
|
||
1.86,1.71,1.14,1.43,
|
||
2.65,1.46,1.15,1.30,
|
||
1.85,1.21,1.12,1.08),
|
||
min = c(1.00,1.00,1.00,1.00,
|
||
1.00,1.00,1.00,1.00,
|
||
1.00,1.00,1.00,1.00,
|
||
1.00,1.00,1.00,1.00,
|
||
1.00,1.00,1.00,1.00,
|
||
1.00,1.00,1.00,1.00),
|
||
max = c(4.50,3.77,3.82,3.29,
|
||
4.07,3.21,2.64,2.86,
|
||
4.80,3.00,2.40,2.80,
|
||
4.14,4.29,3.14,3.71,
|
||
5.00,5.00,4.00,3.63,
|
||
4.64,3.50,3.52,2.89),
|
||
cronbachs_alpha = c(0.87,0.87,0.93,0.88,
|
||
0.80,0.81,0.82,0.82,
|
||
0.75,0.69,0.61,0.66,
|
||
0.78,0.83,0.82,0.82,
|
||
0.96,0.88,0.91,0.90,
|
||
0.95,0.67,0.91,0.90),
|
||
stringsAsFactors = FALSE
|
||
)
|
||
|
||
perzentile_acq_bsq_mi = data.frame(
|
||
skala = rep(c("BSQ","ACQ_gesamt","ACQ_koerperliche_krise","ACQ_kontrollverlust","MIA","MIB"),
|
||
each = 19 * 4),
|
||
perzentil = rep(rep(seq(5, 95, 5), each = 4), times = 6),
|
||
gruppe = rep(rep(c("Paniksyndrom","Andere Angststoerungen",
|
||
"Psychosomat. Patienten ohne Angststoerung",
|
||
"Kontrollpersonen ohne psychische Stoerung"), times = 19), times = 6),
|
||
wert = c(
|
||
1.41,1.17,1.00,1.00, 1.65,1.29,1.00,1.00, 1.82,1.35,1.06,1.12, 2.00,1.47,1.12,1.18,
|
||
2.06,1.53,1.18,1.24, 2.18,1.59,1.24,1.29, 2.29,1.65,1.28,1.35, 2.41,1.77,1.31,1.41,
|
||
2.47,1.82,1.39,1.47, 2.59,2.00,1.53,1.59, 2.67,2.06,1.53,1.65, 2.77,2.12,1.59,1.71,
|
||
2.82,2.29,1.71,1.77, 2.94,2.35,1.82,1.89, 3.12,2.47,1.94,2.00, 3.29,2.59,2.08,2.19,
|
||
3.44,2.72,2.35,2.24, 3.59,3.06,2.62,2.42, 3.80,3.24,3.07,2.53,
|
||
1.14,1.07,1.00,1.00, 1.29,1.07,1.00,1.00, 1.37,1.14,1.00,1.07, 1.46,1.14,1.02,1.07,
|
||
1.57,1.21,1.07,1.07, 1.60,1.36,1.07,1.14, 1.71,1.43,1.07,1.14, 1.79,1.43,1.14,1.14,
|
||
1.86,1.50,1.14,1.21, 1.93,1.57,1.21,1.29, 2.00,1.57,1.29,1.29, 2.07,1.71,1.29,1.29,
|
||
2.14,1.71,1.36,1.36, 2.29,1.86,1.50,1.36, 2.43,1.93,1.50,1.43, 2.57,2.00,1.64,1.50,
|
||
2.64,2.14,1.71,1.62, 2.86,2.29,1.79,1.86, 3.23,2.64,1.93,2.00,
|
||
1.0,1.0,1.0,1.0, 1.0,1.0,1.0,1.0, 1.2,1.0,1.0,1.0, 1.4,1.0,1.0,1.0,
|
||
1.4,1.0,1.0,1.0, 1.6,1.0,1.0,1.0, 1.6,1.0,1.0,1.0, 1.8,1.0,1.0,1.0,
|
||
1.8,1.2,1.0,1.0, 2.0,1.2,1.0,1.0, 2.0,1.2,1.2,1.0, 2.2,1.2,1.2,1.0,
|
||
2.4,1.4,1.2,1.0, 2.4,1.4,1.4,1.0, 2.6,1.6,1.4,1.2, 2.8,1.8,1.6,1.2,
|
||
3.0,1.8,1.6,1.4, 3.4,2.0,1.8,1.4, 3.8,2.2,2.0,1.6,
|
||
1.00,1.00,1.00,1.00, 1.14,1.14,1.00,1.00, 1.14,1.14,1.00,1.00, 1.29,1.29,1.00,1.00,
|
||
1.43,1.29,1.00,1.14, 1.50,1.43,1.00,1.14, 1.57,1.57,1.00,1.29, 1.71,1.71,1.14,1.29,
|
||
1.71,1.71,1.14,1.29, 1.86,1.71,1.14,1.43, 1.86,1.86,1.29,1.43, 2.00,1.86,1.29,1.43,
|
||
2.14,2.00,1.43,1.57, 2.29,2.14,1.44,1.57, 2.43,2.29,1.57,1.71, 2.57,2.57,1.71,1.71,
|
||
2.71,2.71,1.86,1.95, 3.00,3.00,2.14,2.29, 3.29,3.43,2.43,2.57,
|
||
1.10,1.00,1.00,1.00, 1.25,1.03,1.00,1.00, 1.50,1.11,1.00,1.00, 1.66,1.15,1.00,1.04,
|
||
1.82,1.20,1.04,1.08, 2.04,1.26,1.07,1.11, 2.19,1.33,1.10,1.15, 2.33,1.38,1.11,1.19,
|
||
2.48,1.41,1.15,1.26, 2.65,1.46,1.15,1.30, 2.78,1.55,1.18,1.33, 2.97,1.62,1.25,1.41,
|
||
3.15,1.71,1.29,1.48, 3.34,1.84,1.37,1.56, 3.54,1.90,1.48,1.67, 3.70,2.27,1.67,1.75,
|
||
3.91,2.33,2.10,1.93, 4.10,2.71,2.37,2.15, 4.52,3.06,3.01,2.54,
|
||
1.00,1.00,1.00,1.00, 1.04,1.00,1.00,1.00, 1.12,1.00,1.00,1.00, 1.17,1.04,1.00,1.00,
|
||
1.27,1.04,1.00,1.00, 1.31,1.08,1.00,1.00, 1.43,1.08,1.01,1.04, 1.58,1.12,1.04,1.04,
|
||
1.73,1.15,1.08,1.07, 1.85,1.21,1.12,1.08, 1.92,1.28,1.12,1.08, 2.06,1.34,1.14,1.15,
|
||
2.12,1.37,1.15,1.19, 2.31,1.44,1.29,1.23, 2.47,1.57,1.35,1.27, 2.60,1.75,1.60,1.35,
|
||
2.81,1.90,1.68,1.46, 3.04,2.19,1.93,1.65, 3.67,2.47,2.56,2.12
|
||
),
|
||
stringsAsFactors = FALSE
|
||
)
|
||
|
||
stanine_acq_bsq_mi = data.frame(
|
||
skala = rep(c("BSQ","ACQ_gesamt","ACQ_koerperliche_krise",
|
||
"ACQ_kontrollverlust","MIA","MIB"), each = 9 * 4),
|
||
stanine = rep(rep(1:9, each = 4), times = 6),
|
||
gruppe = rep(rep(c("Paniksyndrom","Andere Angststoerungen",
|
||
"Psychosomat. Patienten ohne Angststoerung",
|
||
"Kontrollpersonen ohne psychische Stoerung"), times = 9), times = 6),
|
||
untergrenze = c(
|
||
1.000,1.000,1.000,1.000, 1.370,1.119,1.000,1.000, 1.707,1.295,1.000,1.000,
|
||
2.007,1.488,1.149,1.182, 2.413,1.766,1.307,1.413, 2.766,2.123,1.589,1.707,
|
||
3.198,2.514,2.001,2.060, 3.530,2.947,2.585,2.413, 3.867,3.302,3.170,2.589,
|
||
1.000,1.000,1.000,1.000, 1.144,1.000,1.000,1.000, 1.287,1.072,1.000,1.000,
|
||
1.501,1.215,1.000,1.072, 1.787,1.430,1.144,1.215, 2.072,1.715,1.287,1.287,
|
||
2.501,1.930,1.572,1.430, 2.799,2.258,1.787,1.787, 3.344,2.715,1.978,2.118,
|
||
1.00,1.00,1.00,1.00, 1.00,1.00,1.00,1.00, 1.21,1.00,1.00,1.00,
|
||
1.41,1.00,1.00,1.00, 1.81,1.00,1.00,1.00, 2.21,1.21,1.21,1.00,
|
||
2.61,1.61,1.41,1.21, 3.41,2.01,1.61,1.41, 3.81,2.21,2.01,1.81,
|
||
1.000,1.000,1.000,1.000, 1.000,1.000,1.000,1.000, 1.144,1.144,1.000,1.000,
|
||
1.342,1.287,1.000,1.144, 1.715,1.715,1.144,1.287, 2.001,1.858,1.287,1.430,
|
||
2.572,2.430,1.715,1.715, 2.921,2.944,2.144,2.145, 3.287,3.487,2.430,2.664,
|
||
1.000,1.000,1.000,1.000, 1.026,1.000,1.000,1.000, 1.305,1.080,1.000,1.000,
|
||
1.801,1.186,1.000,1.075, 2.334,1.380,1.112,1.190, 2.971,1.623,1.249,1.408,
|
||
3.588,1.963,1.496,1.685, 4.045,2.691,2.171,2.149, 4.619,3.077,3.263,2.601,
|
||
1.000,1.000,1.000,1.000, 1.000,1.000,1.000,1.000, 1.039,1.000,1.000,1.000,
|
||
1.229,1.039,1.000,1.000, 1.584,1.116,1.041,1.039, 2.061,1.342,1.137,1.151,
|
||
2.544,1.660,1.370,1.270, 3.001,2.076,1.913,1.616, 3.726,2.541,2.601,2.158
|
||
),
|
||
obergrenze = c(
|
||
1.369,1.118,5.000,5.000, 1.706,1.294,5.000,5.000, 2.006,1.487,1.148,1.181,
|
||
2.412,1.765,1.306,1.412, 2.765,2.122,1.588,1.706, 3.197,2.513,2.000,2.059,
|
||
3.529,2.946,2.584,2.412, 3.866,3.301,3.169,2.588, 5.000,5.000,5.000,5.000,
|
||
1.143,5.000,5.000,5.000, 1.286,1.071,5.000,5.000, 1.500,1.214,5.000,1.071,
|
||
1.786,1.429,1.143,1.214, 2.071,1.714,1.286,1.286, 2.500,1.929,1.571,1.429,
|
||
2.798,2.257,1.786,1.786, 3.343,2.714,1.977,2.117, 5.000,5.000,5.000,5.000,
|
||
1.00,1.00,1.00,1.00, 1.20,1.00,1.00,1.00, 1.40,1.00,1.00,1.00,
|
||
1.80,1.00,1.00,1.00, 2.20,1.20,1.20,1.00, 2.60,1.60,1.40,1.20,
|
||
3.40,2.00,1.60,1.40, 3.80,2.20,2.00,1.80, 5.00,5.00,5.00,5.00,
|
||
5.000,5.000,5.000,5.000, 1.143,1.143,5.000,5.000, 1.341,1.286,1.000,1.143,
|
||
1.714,1.714,1.143,1.286, 2.000,1.857,1.286,1.429, 2.571,2.429,1.714,1.714,
|
||
2.920,2.943,2.143,2.144, 3.286,3.486,2.429,2.663, 5.000,5.000,5.000,5.000,
|
||
1.025,5.000,5.000,5.000, 1.304,1.079,5.000,5.000, 1.800,1.185,1.000,1.074,
|
||
2.333,1.379,1.111,1.189, 2.970,1.622,1.248,1.407, 3.587,1.962,1.495,1.684,
|
||
4.044,2.690,2.170,2.148, 4.618,3.076,3.262,2.600, 5.000,5.000,5.000,5.000,
|
||
5.000,5.000,5.000,5.000, 1.038,5.000,5.000,5.000, 1.228,1.038,1.000,1.038,
|
||
1.583,1.115,1.040,1.038, 2.060,1.341,1.136,1.150, 2.543,1.659,1.369,1.269,
|
||
3.000,2.075,1.912,1.615, 3.725,2.540,2.600,2.157, 5.000,5.000,5.000,5.000
|
||
),
|
||
stringsAsFactors = FALSE
|
||
)
|
||
|
||
|
||
# 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; }
|
||
.btn-laden {
|
||
background: #8B2635 !important; color: white !important;
|
||
border: none !important; border-radius: 4px !important;
|
||
padding: 8px 20px !important; font-weight: 600 !important;
|
||
}
|
||
.btn-laden:hover { background: #6d1e29 !important; }
|
||
.alert-fehler {
|
||
background: #FFEBEE; border-left: 5px solid #C62828;
|
||
padding: 12px 16px; border-radius: 4px; color: #B71C1C;
|
||
margin-bottom: 12px; font-weight: 500;
|
||
}
|
||
.alert-warnung {
|
||
background: #FFF3E0; border-left: 5px solid #E65100;
|
||
padding: 10px 16px; border-radius: 4px; color: #BF360C;
|
||
margin-bottom: 8px; font-size: 0.93em; font-weight: 500;
|
||
}
|
||
.abschnitt-karte {
|
||
background: white; border-radius: 6px; padding: 20px 24px;
|
||
margin-bottom: 16px; box-shadow: 0 1px 3px rgba(0,0,0,.12);
|
||
}
|
||
.abschnitt-titel {
|
||
color: #8B2635; font-size: 1.15rem; font-weight: 700;
|
||
border-bottom: 2px solid #8B2635; padding-bottom: 8px; margin-bottom: 14px;
|
||
}
|
||
.meta-block { margin-bottom: 10px; color: #555; font-size: 0.95em; }
|
||
.meta-block strong { color: #222; }
|
||
.btn-laden { cursor: pointer; }
|
||
.item-zeile {
|
||
display: flex; align-items: flex-start; gap: 10px;
|
||
padding: 7px 0; border-bottom: 1px solid #F0F0F0;
|
||
}
|
||
.item-nr { font-weight: 600; color: #8B2635; min-width: 28px; flex-shrink: 0; font-size: 0.92em; }
|
||
.item-text { flex: 1; color: #333; font-size: 0.92em; line-height: 1.4; }
|
||
.score-zahl { font-size: 2.2rem; font-weight: 800; color: #8B2635; line-height: 1.1; }
|
||
.fussnote { font-size: 0.78em; color: #888; font-style: italic; margin-top: 10px; }
|
||
html { min-width: 1400px; }
|
||
.container-fluid { max-width: 1600px !important; margin: 0 auto !important; }
|
||
"
|
||
|
||
app_css = gsub("#8B2635", AKZENT_FARBE, app_css, fixed = TRUE)
|
||
|
||
ui = fluidPage(
|
||
tags$head(
|
||
tags$meta(charset = "UTF-8"),
|
||
tags$style(HTML(app_css))
|
||
),
|
||
|
||
div(class = "app-header",
|
||
tags$h2("ACQ / BSQ / Mobilitaets-Inventar"),
|
||
tags$p("Fragebogen zu angstbezogenen Kognitionen, Angst vor koerperlichen Symptomen",
|
||
" und Mobilitaets-Inventar | Ehlers & Margraf")
|
||
),
|
||
|
||
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: 200px;",
|
||
textInput("chiffre", label = "Patientenchiffre",
|
||
placeholder = "z.B. P000123", width = "100%")
|
||
),
|
||
actionButton("btn_suchen", "Auswerten", class = "btn btn-primary btn-laden"),
|
||
div(style = "margin-left: auto;",
|
||
downloadButton("download_word", "Word-Export (.docx)")
|
||
)
|
||
),
|
||
|
||
uiOutput("fehler_ui"),
|
||
uiOutput("warnung_ui"),
|
||
uiOutput("ergebnis_ui")
|
||
)
|
||
)
|
||
|
||
|
||
# Word-Export ####
|
||
|
||
erstelle_acqbsqmi_docx = function(erg) {
|
||
doc = read_docx()
|
||
|
||
fp_titel = fp_text(color = AKZENT_FARBE, bold = TRUE, font.size = 18)
|
||
fp_abschnitt = fp_text(color = AKZENT_FARBE, bold = TRUE, font.size = 13)
|
||
fp_label = fp_text(bold = TRUE, font.size = 11)
|
||
fp_normal = fp_text(font.size = 11)
|
||
fp_weitere = fp_text(font.size = 11, italic = TRUE, bold = TRUE, color = "#666666")
|
||
fp_klein = fp_text(font.size = 9.5, color = "#555555")
|
||
fp_disc = fp_text(font.size = 9, italic = TRUE, color = "#777777")
|
||
fp_warn = fp_text(font.size = 10, color = "#BF360C")
|
||
|
||
acq_bsq_badge_fp = function(wert_num) {
|
||
farben = list(
|
||
"1" = list(bg = "#4CAF50", fg = "white"),
|
||
"2" = list(bg = "#F48FB1", fg = "#333333"),
|
||
"3" = list(bg = "#EF5350", fg = "white"),
|
||
"4" = list(bg = "#B71C1C", fg = "white"),
|
||
"5" = list(bg = "#4A0000", fg = "white")
|
||
)
|
||
key = if (is.na(wert_num)) "1" else
|
||
as.character(min(5L, max(1L, as.integer(round(wert_num)))))
|
||
f = farben[[key]]
|
||
fp_text(bold = TRUE, font.size = 10, color = f$fg, shading.color = f$bg)
|
||
}
|
||
|
||
score_und_normen = function(label, score_info, skala_key) {
|
||
score_txt = if (is.na(score_info$score)) "k. A." else sprintf("%.2f", score_info$score)
|
||
doc <<- body_add_fpar(doc, fpar(
|
||
ftext(paste0(label, ": "), fp_label),
|
||
ftext(score_txt, fp_normal)
|
||
))
|
||
if (!is.null(score_info$warnung))
|
||
doc <<- body_add_fpar(doc, fpar(ftext(score_info$warnung, fp_warn)))
|
||
doc <<- body_add_fpar(doc, fpar(
|
||
ftext(normen_text_word(score_info$score, skala_key), fp_klein)))
|
||
}
|
||
|
||
word_rang_acq_bsq = function(items, n_score, kurzlabels, weitere_text) {
|
||
alle_items = items[seq_len(min(n_score + 1L, length(items)))]
|
||
sortiert = alle_items[order(sapply(alle_items, function(x)
|
||
if (is.na(x$wert_num)) Inf else -x$wert_num
|
||
))]
|
||
make_badge_txt = function(item) {
|
||
if (!is.null(kurzlabels) && !is.na(item$wert_num)) {
|
||
idx = as.integer(round(item$wert_num))
|
||
if (idx >= 1L && idx <= length(kurzlabels)) return(kurzlabels[idx])
|
||
}
|
||
if (is.na(item$wert_text)) "k.A." else item$wert_text
|
||
}
|
||
rang_nr = 0L
|
||
for (item in sortiert) {
|
||
is_w = !item$in_score
|
||
if (!is_w) rang_nr = rang_nr + 1L
|
||
nr_label = if (is_w) "zus." else paste0(rang_nr, ".")
|
||
item_txt = if (is_w) {
|
||
if (!is.null(weitere_text)) paste0("Weitere: ", weitere_text)
|
||
else if (!is.na(item$text)) item$text else "Weitere"
|
||
} else {
|
||
paste0("(", item$nr, ") ",
|
||
if (is.na(item$text)) paste0("Item ", item$nr) else item$text)
|
||
}
|
||
fp_item = if (is_w) fp_weitere else fp_normal
|
||
if (!is.na(item$wert_num)) {
|
||
doc <<- body_add_fpar(doc, fpar(
|
||
ftext(paste0(nr_label, " ", item_txt, " "), fp_item),
|
||
ftext(paste0(" ", make_badge_txt(item), " "), acq_bsq_badge_fp(item$wert_num))
|
||
))
|
||
} else {
|
||
doc <<- body_add_fpar(doc, fpar(
|
||
ftext(paste0(nr_label, " ", item_txt), fp_item)))
|
||
}
|
||
}
|
||
}
|
||
|
||
word_rang_mi = function(mi_items) {
|
||
score_items = Filter(function(x) x$in_score, mi_items)
|
||
sortiert = score_items[order(sapply(score_items, function(x)
|
||
if (!x$zutreffend || is.na(x$allein_num)) Inf else -x$allein_num
|
||
))]
|
||
for (item in sortiert) {
|
||
sit_txt = if (is.na(item$text)) paste0("Situation ", item$nr) else item$text
|
||
if (!item$zutreffend) {
|
||
doc <<- body_add_fpar(doc, fpar(
|
||
ftext(paste0(item$nr, ". ", sit_txt, " – betrifft nicht"), fp_weitere)))
|
||
} else {
|
||
delta = if (!is.na(item$allein_num) && !is.na(item$begl_num))
|
||
item$allein_num - item$begl_num else NA_real_
|
||
delta_s = if (is.na(delta)) "-" else sprintf("%+.1f", delta)
|
||
fp_delta = if (!is.na(delta) && delta >= 1.5)
|
||
fp_text(font.size = 10, bold = TRUE, color = "#B71C1C")
|
||
else if (!is.na(delta) && delta >= 0.5)
|
||
fp_text(font.size = 10, bold = TRUE, color = "#E65100")
|
||
else fp_klein
|
||
doc <<- body_add_fpar(doc, fpar(
|
||
ftext(paste0(item$nr, ". ", sit_txt, " "), fp_normal),
|
||
ftext(paste0("Allein: ", sprintf("%.1f", item$allein_num),
|
||
" | Begl: ", sprintf("%.1f", item$begl_num),
|
||
" | Delta: ", delta_s), fp_delta)
|
||
))
|
||
}
|
||
}
|
||
}
|
||
|
||
PLOT_W = 14 / 2.54
|
||
PLOT_H = 6 / 2.54
|
||
|
||
# Titel + Metadaten
|
||
doc = body_add_fpar(doc, fpar(
|
||
ftext("ACQ / BSQ / Mobilitaets-Inventar - Einzelauswertung", fp_titel)))
|
||
doc = body_add_fpar(doc, fpar(
|
||
ftext("Chiffre: ", fp_label),
|
||
ftext(erg$chiffre, fp_normal),
|
||
ftext(" Datum: ", fp_label),
|
||
ftext(erg$ausfuelldatum, fp_normal)
|
||
))
|
||
if (!is.null(erg$warnung_mehrere))
|
||
doc = body_add_fpar(doc, fpar(ftext(erg$warnung_mehrere, fp_klein)))
|
||
doc = body_add_par(doc, "", style = "Normal")
|
||
|
||
# ACQ Gesamt
|
||
doc = body_add_fpar(doc, fpar(ftext("ACQ Gesamtmittelwert", fp_abschnitt)))
|
||
score_und_normen("ACQ Gesamt", erg$acq_gesamt, "ACQ_gesamt")
|
||
doc = body_add_gg(doc,
|
||
value = make_vergleich_plot(erg$acq_gesamt$score, "ACQ_gesamt", "ACQ Gesamt"),
|
||
width = PLOT_W, height = PLOT_H)
|
||
word_rang_acq_bsq(erg$acq_items, 14,
|
||
c("nie", "selten", "~Haelfte", "gewoehnlich", "immer"),
|
||
erg$acq_15_text)
|
||
doc = body_add_par(doc, "", style = "Normal")
|
||
|
||
# ACQ Faktoren
|
||
doc = body_add_fpar(doc, fpar(ftext("ACQ Faktor: Koerperliche Krise", fp_abschnitt)))
|
||
score_und_normen("Koerperliche Krise", erg$acq_koerper, "ACQ_koerperliche_krise")
|
||
doc = body_add_gg(doc,
|
||
value = make_vergleich_plot(erg$acq_koerper$score, "ACQ_koerperliche_krise",
|
||
"ACQ Koerperliche Krise"),
|
||
width = PLOT_W, height = PLOT_H)
|
||
doc = body_add_par(doc, "", style = "Normal")
|
||
|
||
doc = body_add_fpar(doc, fpar(ftext("ACQ Faktor: Kontrollverlust", fp_abschnitt)))
|
||
score_und_normen("Kontrollverlust", erg$acq_kontroll, "ACQ_kontrollverlust")
|
||
doc = body_add_gg(doc,
|
||
value = make_vergleich_plot(erg$acq_kontroll$score, "ACQ_kontrollverlust",
|
||
"ACQ Kontrollverlust"),
|
||
width = PLOT_W, height = PLOT_H)
|
||
doc = body_add_par(doc, "", style = "Normal")
|
||
|
||
# BSQ
|
||
doc = body_add_fpar(doc, fpar(ftext("BSQ Gesamtmittelwert", fp_abschnitt)))
|
||
score_und_normen("BSQ Gesamt", erg$bsq_gesamt, "BSQ")
|
||
doc = body_add_gg(doc,
|
||
value = make_vergleich_plot(erg$bsq_gesamt$score, "BSQ", "BSQ Gesamt"),
|
||
width = PLOT_W, height = PLOT_H)
|
||
word_rang_acq_bsq(erg$bsq_items, 17,
|
||
c("nicht", "ein wenig", "mittelmaessig", "sehr", "extrem"),
|
||
erg$bsq_18_text)
|
||
doc = body_add_par(doc, "", style = "Normal")
|
||
|
||
# MI
|
||
doc = body_add_fpar(doc, fpar(ftext("Mobilitaets-Inventar", fp_abschnitt)))
|
||
score_und_normen("MIA (Vermeidung allein)", erg$mia, "MIA")
|
||
doc = body_add_gg(doc,
|
||
value = make_vergleich_plot(erg$mia$score, "MIA", "MI - Allein"),
|
||
width = PLOT_W, height = PLOT_H)
|
||
score_und_normen("MIB (Vermeidung in Begleitung)", erg$mib, "MIB")
|
||
doc = body_add_gg(doc,
|
||
value = make_vergleich_plot(erg$mib$score, "MIB", "MI - Begleitung"),
|
||
width = PLOT_W, height = PLOT_H)
|
||
word_rang_mi(erg$mi_items)
|
||
doc = body_add_par(doc, "", style = "Normal")
|
||
|
||
doc = body_add_fpar(doc, fpar(ftext(ACQBSQMI_DISCLAIMER, fp_disc)))
|
||
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)))
|
||
}
|
||
})
|
||
|
||
ergebnis_r = eventReactive(input$btn_suchen, {
|
||
|
||
chiffre = toupper(trimws(input$chiffre))
|
||
|
||
if ((nchar(trimws(input$pseudonym)) == 0 && nchar(chiffre) == 0))
|
||
return(list(typ = "format_fehler",
|
||
meldung = "Bitte eine Patientenchiffre eingeben."))
|
||
|
||
if (!(nchar(trimws(input$pseudonym)) > 0 || grepl("^[A-Z][0-9]{6}$", chiffre)))
|
||
return(list(typ = "format_fehler",
|
||
meldung = paste0("Ungueltige Chiffre '", chiffre,
|
||
"'. Erwartet: ein Grossbuchstabe + 6 Ziffern (z.B. P000123).")))
|
||
|
||
if (!file.exists(PFAD_DOWNLOAD_SKRIPT))
|
||
return(list(typ = "skript_fehler",
|
||
meldung = paste0("Download-Skript nicht gefunden:\n", PFAD_DOWNLOAD_SKRIPT)))
|
||
|
||
if (!file.exists(PFAD_PSEUDONYM_SKRIPT))
|
||
return(list(typ = "skript_fehler",
|
||
meldung = paste0("Pseudonym-Skript nicht gefunden:\n", PFAD_PSEUDONYM_SKRIPT)))
|
||
|
||
ok = tryCatch({
|
||
source(PFAD_DOWNLOAD_SKRIPT, local = FALSE)
|
||
list(ok = TRUE)
|
||
}, error = function(e) list(ok = FALSE, msg = e$message))
|
||
if (!ok$ok)
|
||
return(list(typ = "skript_fehler",
|
||
meldung = paste0("Fehler im Download-Skript: ", ok$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
|
||
})
|
||
|
||
alter_wd = getwd()
|
||
on.exit(setwd(alter_wd), add = TRUE)
|
||
wd_ziel = if (!is.null(db_ordner)) db_ordner else
|
||
dirname(normalizePath(PFAD_PSEUDONYM_SKRIPT, mustWork = FALSE))
|
||
setwd(wd_ziel)
|
||
|
||
ok2 = 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 (!ok2$ok)
|
||
return(list(typ = "skript_fehler",
|
||
meldung = paste0("Fehler im Pseudonym-Skript: ", ok2$msg)))
|
||
|
||
if (!exists("daten_acqbsqmi", envir = .GlobalEnv))
|
||
return(list(typ = "skript_fehler",
|
||
meldung = paste0("Objekt 'daten_acqbsqmi' nach Sourcen nicht gefunden. ",
|
||
"Bitte Download-Skript pruefen.")))
|
||
if (!exists("pseudo", envir = .GlobalEnv))
|
||
return(list(typ = "skript_fehler",
|
||
meldung = paste0("Objekt 'pseudo' nach Sourcen nicht gefunden. ",
|
||
"Bitte Pseudonym-Skript pruefen.")))
|
||
|
||
daten = get("daten_acqbsqmi", envir = .GlobalEnv)
|
||
pseudo_df = get("pseudo", envir = .GlobalEnv)
|
||
|
||
treffer_ps = pseudo_df[pseudo_df$chiffre == chiffre, ]
|
||
if (nrow(treffer_ps) == 0)
|
||
return(list(typ = "chiffre_fehler",
|
||
meldung = paste0("Chiffre '", chiffre,
|
||
"' wurde in der Pseudonym-Datenbank nicht gefunden.")))
|
||
|
||
alle_session_ids = unique(treffer_ps$pseudonym)
|
||
if (nchar(trimws(input$pseudonym)) > 0) alle_session_ids = trimws(input$pseudonym)
|
||
treffer_dat = daten[daten$session %in% alle_session_ids, ]
|
||
if (nrow(treffer_dat) == 0)
|
||
return(list(typ = "chiffre_fehler",
|
||
meldung = paste0("Bogen fuer Chiffre '", chiffre,
|
||
"' noch nicht ausgefuellt. ",
|
||
"(", length(alle_session_ids), " Session(s) geprueft)")))
|
||
|
||
info_mehrere = NULL
|
||
if (nrow(treffer_dat) > 1) {
|
||
n = nrow(treffer_dat)
|
||
treffer_dat = treffer_dat[order(treffer_dat$created, decreasing = TRUE), ]
|
||
datum_neu = tryCatch(
|
||
format(as.POSIXct(treffer_dat$created[1]), "%d.%m.%Y %H:%M"),
|
||
error = function(e) "unbekanntes Datum"
|
||
)
|
||
info_mehrere = paste0("Mehrere Ausfuellungen gefunden (", n, " Eintraege). ",
|
||
"Angezeigt wird die neueste vom ", datum_neu, ".")
|
||
treffer_dat = treffer_dat[1, , drop = FALSE]
|
||
}
|
||
|
||
zeile = treffer_dat[1, , drop = FALSE]
|
||
|
||
ausfuelldatum_str = tryCatch(
|
||
format(as.POSIXct(zeile[["created"]][1]), "%d.%m.%Y"),
|
||
error = function(e) format(Sys.Date(), "%d.%m.%Y")
|
||
)
|
||
|
||
acq_items = lapply(1:15, function(i) {
|
||
var = paste0("acq_", sprintf("%02d", i))
|
||
w = get_numeric_wert(zeile[[var]])
|
||
list(
|
||
nr = i,
|
||
nr_label = if (i <= 14) paste0(i, ".") else "",
|
||
text = clean_label(attr(daten[[var]], "label")),
|
||
wert_num = w,
|
||
wert_text = get_label_text(daten[[var]], zeile[[var]]),
|
||
in_score = i <= 14
|
||
)
|
||
})
|
||
|
||
acq_werte_gesamt = sapply(1:14, function(i)
|
||
get_numeric_wert(zeile[[paste0("acq_", sprintf("%02d", i))]]))
|
||
acq_gesamt = berechne_mittelwert_mit_warnung(acq_werte_gesamt, max_fehlend = 3)
|
||
|
||
acq_werte_koerper = sapply(c(2, 3, 4, 5, 10), function(i)
|
||
get_numeric_wert(zeile[[paste0("acq_", sprintf("%02d", i))]]))
|
||
acq_koerper = berechne_mittelwert_mit_warnung(acq_werte_koerper, max_fehlend = 1)
|
||
|
||
acq_werte_kontroll = sapply(c(6, 8, 9, 11, 12, 13, 14), function(i)
|
||
get_numeric_wert(zeile[[paste0("acq_", sprintf("%02d", i))]]))
|
||
acq_kontroll = berechne_mittelwert_mit_warnung(acq_werte_kontroll, max_fehlend = 1)
|
||
|
||
bsq_items = lapply(1:18, function(i) {
|
||
var = paste0("bsq_", sprintf("%02d", i))
|
||
w = get_numeric_wert(zeile[[var]])
|
||
list(
|
||
nr = i,
|
||
nr_label = if (i <= 17) paste0(i, ".") else "",
|
||
text = clean_label(attr(daten[[var]], "label")),
|
||
wert_num = w,
|
||
wert_text = get_label_text(daten[[var]], zeile[[var]]),
|
||
in_score = i <= 17
|
||
)
|
||
})
|
||
|
||
bsq_werte = sapply(1:17, function(i)
|
||
get_numeric_wert(zeile[[paste0("bsq_", sprintf("%02d", i))]]))
|
||
bsq_gesamt = berechne_mittelwert_mit_warnung(bsq_werte, max_fehlend = 3)
|
||
|
||
mi_items = lapply(1:28, function(i) {
|
||
var_z = paste0("mi_", sprintf("%02d", i), "_zutreffend")
|
||
var_b = paste0("mi_", sprintf("%02d", i), "_begl")
|
||
var_a = paste0("mi_", sprintf("%02d", i), "_allein")
|
||
ztref = mi_ist_zutreffend(daten[[var_z]], zeile[[var_z]])
|
||
begl_n = if (ztref) get_numeric_wert(zeile[[var_b]]) else NA_real_
|
||
allein_n = if (ztref) get_numeric_wert(zeile[[var_a]]) else NA_real_
|
||
list(
|
||
nr = i,
|
||
nr_label = if (i <= 27) paste0(i, ".") else "",
|
||
text = if (i <= length(MI_SITUATIONEN)) MI_SITUATIONEN[i] else NA_character_,
|
||
zutreffend = ztref,
|
||
begl_num = begl_n,
|
||
allein_num = allein_n,
|
||
in_score = i <= 27
|
||
)
|
||
})
|
||
|
||
werte_allein = sapply(1:27, function(i) mi_items[[i]]$allein_num)
|
||
werte_begl = sapply(1:27, function(i) mi_items[[i]]$begl_num)
|
||
n_valid_allein = sum(!is.na(werte_allein))
|
||
n_valid_begl = sum(!is.na(werte_begl))
|
||
mia_score = if (n_valid_allein == 0) NA_real_ else mean(werte_allein, na.rm = TRUE)
|
||
mib_score = if (n_valid_begl == 0) NA_real_ else mean(werte_begl, na.rm = TRUE)
|
||
mia_warnung = if (!is.na(mia_score) && n_valid_allein < 14)
|
||
paste0("Auswertung auf Basis von nur ", n_valid_allein,
|
||
" von 27 Situationen. Vorsicht bei der Interpretation.")
|
||
else NULL
|
||
mib_warnung = if (!is.na(mib_score) && n_valid_begl < 14)
|
||
paste0("Auswertung auf Basis von nur ", n_valid_begl,
|
||
" von 27 Situationen. Vorsicht bei der Interpretation.")
|
||
else NULL
|
||
mia = list(score = mia_score, n_vorhanden = n_valid_allein, warnung = mia_warnung)
|
||
mib = list(score = mib_score, n_vorhanden = n_valid_begl, warnung = mib_warnung)
|
||
|
||
list(
|
||
typ = "ok",
|
||
chiffre = chiffre,
|
||
ausfuelldatum = ausfuelldatum_str,
|
||
warnung_mehrere = info_mehrere,
|
||
acq_gesamt = acq_gesamt,
|
||
acq_koerper = acq_koerper,
|
||
acq_kontroll = acq_kontroll,
|
||
bsq_gesamt = bsq_gesamt,
|
||
mia = mia,
|
||
mib = mib,
|
||
acq_items = acq_items,
|
||
acq_15_text = raw_to_char_or_null(zeile[["acq_15_text"]]),
|
||
acq_top3 = raw_to_char_or_null(zeile[["acq_top3"]]),
|
||
bsq_items = bsq_items,
|
||
bsq_18_text = raw_to_char_or_null(zeile[["bsq_18_text"]]),
|
||
bsq_top3 = raw_to_char_or_null(zeile[["bsq_top3"]]),
|
||
mi_items = mi_items,
|
||
mi_28_text = raw_to_char_or_null(zeile[["mi_28_text"]])
|
||
)
|
||
})
|
||
|
||
output$fehler_ui = renderUI({
|
||
req(input$btn_suchen)
|
||
d = ergebnis_r()
|
||
if (!is.null(d$typ) && d$typ != "ok")
|
||
div(class = "alert-fehler", d$meldung)
|
||
else NULL
|
||
})
|
||
|
||
output$warnung_ui = renderUI({
|
||
req(input$btn_suchen)
|
||
d = ergebnis_r()
|
||
if (is.null(d$typ) || d$typ != "ok" || is.null(d$warnung_mehrere)) return(NULL)
|
||
div(class = "alert-warnung", d$warnung_mehrere)
|
||
})
|
||
|
||
output$ergebnis_ui = renderUI({
|
||
req(input$btn_suchen)
|
||
d = ergebnis_r()
|
||
if (is.null(d$typ) || d$typ != "ok") return(NULL)
|
||
|
||
div(
|
||
|
||
div(class = "abschnitt-karte",
|
||
div(class = "meta-block",
|
||
tags$strong("Chiffre: "), d$chiffre,
|
||
tags$span(" | ", style = "color:#ddd;"),
|
||
tags$strong("Ausfuelldatum: "), d$ausfuelldatum
|
||
),
|
||
|
||
div(class = "abschnitt-titel", "Scores - Uebersicht"),
|
||
fluidRow(
|
||
column(6, score_block_ui(d$acq_gesamt, "ACQ_gesamt", "ACQ Gesamt", "plot_acq_gesamt")),
|
||
column(6, score_block_ui(d$bsq_gesamt, "BSQ", "BSQ Gesamt", "plot_bsq_gesamt"))
|
||
),
|
||
|
||
tags$hr(style = "margin:14px 0;"),
|
||
div(class = "abschnitt-titel", style = "font-size:1rem; margin-bottom:8px;",
|
||
"ACQ Faktoren"),
|
||
fluidRow(
|
||
column(6, score_block_ui(d$acq_koerper, "ACQ_koerperliche_krise",
|
||
"Koerperliche Krise", "plot_acq_koerper")),
|
||
column(6, score_block_ui(d$acq_kontroll, "ACQ_kontrollverlust",
|
||
"Kontrollverlust", "plot_acq_kontroll"))
|
||
),
|
||
|
||
tags$hr(style = "margin:14px 0;"),
|
||
div(class = "abschnitt-titel", style = "font-size:1rem; margin-bottom:8px;",
|
||
"Mobilitaets-Inventar"),
|
||
{
|
||
mi_warns = unique(Filter(Negate(is.null), list(d$mia$warnung, d$mib$warnung)))
|
||
lapply(mi_warns, function(w) div(class = "alert-warnung", w))
|
||
},
|
||
fluidRow(
|
||
column(6,
|
||
div(class = "score-zahl",
|
||
if (is.na(d$mia$score)) "k.A." else sprintf("%.2f", d$mia$score)),
|
||
div("MIA (Allein)", style = "color:#555; font-size:0.82em;"),
|
||
normen_tabelle_html(d$mia$score, "MIA"),
|
||
plotOutput("plot_mia", height = "155px")
|
||
),
|
||
column(6,
|
||
div(class = "score-zahl",
|
||
if (is.na(d$mib$score)) "k.A." else sprintf("%.2f", d$mib$score)),
|
||
div("MIB (Begleitung)", style = "color:#555; font-size:0.82em;"),
|
||
normen_tabelle_html(d$mib$score, "MIB"),
|
||
plotOutput("plot_mib", height = "155px")
|
||
)
|
||
)
|
||
),
|
||
|
||
div(class = "abschnitt-karte",
|
||
div(class = "abschnitt-titel", "Inhaltliche Rangfolge"),
|
||
fluidRow(
|
||
column(4,
|
||
tags$h6(style = "color:#555; margin-bottom:6px;",
|
||
"ACQ - Gedanken nach Haeufigkeit"),
|
||
div(render_rang_acq_bsq(d$acq_items, 14,
|
||
c("nie", "selten", "~Haelfte", "gewoehnlich", "immer"),
|
||
weitere_text = d$acq_15_text))
|
||
),
|
||
column(4,
|
||
tags$h6(style = "color:#555; margin-bottom:6px;",
|
||
"BSQ - Empfindungen nach Belastung"),
|
||
div(render_rang_acq_bsq(d$bsq_items, 17,
|
||
c("nicht", "ein wenig", "mittelmaessig", "sehr", "extrem"),
|
||
weitere_text = d$bsq_18_text))
|
||
),
|
||
column(4,
|
||
tags$h6(style = "color:#555; margin-bottom:6px;",
|
||
"MI - Allein absteigend | Delta = Allein minus Begl."),
|
||
div(render_rang_mi(d$mi_items))
|
||
)
|
||
)
|
||
),
|
||
|
||
)
|
||
})
|
||
|
||
output$plot_acq_gesamt = renderPlot({
|
||
req(input$btn_suchen)
|
||
d = ergebnis_r()
|
||
req(!is.null(d$typ) && d$typ == "ok")
|
||
make_vergleich_plot(d$acq_gesamt$score, "ACQ_gesamt", "ACQ Gesamt")
|
||
}, bg = "transparent")
|
||
|
||
output$plot_bsq_gesamt = renderPlot({
|
||
req(input$btn_suchen)
|
||
d = ergebnis_r()
|
||
req(!is.null(d$typ) && d$typ == "ok")
|
||
make_vergleich_plot(d$bsq_gesamt$score, "BSQ", "BSQ Gesamt")
|
||
}, bg = "transparent")
|
||
|
||
output$plot_mia = renderPlot({
|
||
req(input$btn_suchen)
|
||
d = ergebnis_r()
|
||
req(!is.null(d$typ) && d$typ == "ok")
|
||
make_vergleich_plot(d$mia$score, "MIA", "MIA (Vermeidung allein)")
|
||
}, bg = "transparent")
|
||
|
||
output$plot_mib = renderPlot({
|
||
req(input$btn_suchen)
|
||
d = ergebnis_r()
|
||
req(!is.null(d$typ) && d$typ == "ok")
|
||
make_vergleich_plot(d$mib$score, "MIB", "MIB (Vermeidung in Begleitung)")
|
||
}, bg = "transparent")
|
||
|
||
output$plot_acq_koerper = renderPlot({
|
||
req(input$btn_suchen)
|
||
d = ergebnis_r()
|
||
req(!is.null(d$typ) && d$typ == "ok")
|
||
make_vergleich_plot(d$acq_koerper$score, "ACQ_koerperliche_krise", "Koerperliche Krise")
|
||
}, bg = "transparent")
|
||
|
||
output$plot_acq_kontroll = renderPlot({
|
||
req(input$btn_suchen)
|
||
d = ergebnis_r()
|
||
req(!is.null(d$typ) && d$typ == "ok")
|
||
make_vergleich_plot(d$acq_kontroll$score, "ACQ_kontrollverlust", "Kontrollverlust")
|
||
}, bg = "transparent")
|
||
|
||
output$download_word = downloadHandler(
|
||
filename = function() {
|
||
d = tryCatch(ergebnis_r(), error = function(e) NULL)
|
||
chiffre_esc = if (is.list(d) && !is.null(d$typ) && d$typ == "ok")
|
||
d$chiffre else "export"
|
||
ausfuelldatum_fn = if (is.list(d) && !is.null(d$typ) && d$typ == "ok")
|
||
tryCatch(
|
||
format(as.Date(d$ausfuelldatum, "%d.%m.%Y"), "%Y%m%d"),
|
||
error = function(e) format(Sys.Date(), "%Y%m%d")
|
||
)
|
||
else format(Sys.Date(), "%Y%m%d")
|
||
paste0("ACQBSQMI_", chiffre_esc, "_", ausfuelldatum_fn, ".docx")
|
||
},
|
||
content = function(file) {
|
||
d = tryCatch(ergebnis_r(), error = function(e) NULL)
|
||
if (is.null(d) || is.null(d$typ) || d$typ != "ok") {
|
||
doc = read_docx()
|
||
doc = body_add_par(doc,
|
||
"Kein Datensatz geladen. Bitte zuerst Chiffre eingeben und 'Auswerten' klicken.",
|
||
style = "Normal")
|
||
print(doc, target = file)
|
||
return()
|
||
}
|
||
doc = tryCatch(
|
||
erstelle_acqbsqmi_docx(d),
|
||
error = function(e) {
|
||
err_doc = read_docx()
|
||
body_add_par(err_doc,
|
||
paste0("Fehler beim Erstellen des Word-Dokuments: ", e$message),
|
||
style = "Normal")
|
||
}
|
||
)
|
||
print(doc, target = file)
|
||
}
|
||
)
|
||
}
|
||
|
||
|
||
# Start ####
|
||
|
||
shinyApp(ui, server)
|