Initial commit
This commit is contained in:
commit
3cba772836
1341 changed files with 532924 additions and 0 deletions
BIN
ADHS-Funktionsniveau/.RData
Normal file
BIN
ADHS-Funktionsniveau/.RData
Normal file
Binary file not shown.
1
ADHS-Funktionsniveau/.Rprofile
Normal file
1
ADHS-Funktionsniveau/.Rprofile
Normal file
|
|
@ -0,0 +1 @@
|
|||
source("renv/activate.R")
|
||||
13
ADHS-Funktionsniveau/ADHS-Funktionsniveau.Rproj
Normal file
13
ADHS-Funktionsniveau/ADHS-Funktionsniveau.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
|
||||
870
ADHS-Funktionsniveau/app.R
Normal file
870
ADHS-Funktionsniveau/app.R
Normal file
|
|
@ -0,0 +1,870 @@
|
|||
# Praeambel ####
|
||||
|
||||
AKZENT_FARBE = "#8B2635"
|
||||
FARBE_GUT = "#4CAF50"
|
||||
FARBE_SCHLECHT = "#B71C1C"
|
||||
|
||||
PFAD_DOWNLOAD_SKRIPT = "../API/get_data_adhs_funktionsniveau.R" # liefert: daten_adhs_funktionsniveau
|
||||
PFAD_PSEUDONYM_SKRIPT = "../get_pseudo.R" # liefert: pseudo
|
||||
|
||||
ADHS_FUNKTIONSNIVEAU_DISCLAIMER = paste0(
|
||||
"Diese Auswertung ist ein Hilfsmittel fuer klinisches Fachpersonal und ersetzt ",
|
||||
"keine klinische Diagnose. Es liegen keine publizierten Normwerte oder Cutoffs zu diesem ",
|
||||
"Instrument vor, die Darstellung ist rein deskriptiv. Die Interpretation obliegt der ",
|
||||
"behandelnden Person."
|
||||
)
|
||||
|
||||
ADHS_FUNKTIONSNIVEAU_RICHTUNGSHINWEIS = paste0(
|
||||
"Achtung Codierungsrichtung: Hohe Werte bedeuten ein schlechteres Funktionsniveau, ",
|
||||
"niedrige Werte ein besseres. Dies ist gegenlaeufig zu vielen anderen Instrumenten."
|
||||
)
|
||||
|
||||
library(shiny)
|
||||
library(dplyr)
|
||||
library(ggplot2)
|
||||
library(haven)
|
||||
library(officer)
|
||||
|
||||
|
||||
# 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 ####
|
||||
|
||||
# UNVERIFIZIERTE ANNAHME (siehe Abschnitt 4, Punkt 1 der Spezifikation): Es ist nicht durch
|
||||
# einen echten formr-Testdurchlauf bestaetigt, ob range_ticks-Items numerisch oder als
|
||||
# haven_labelled (dbl+lbl) exportiert werden. Diese Funktion faengt beide Faelle ab und
|
||||
# liefert immer entweder einen validen Wert 1-10 oder einen expliziten Status.
|
||||
hole_range_wert = function(rohwert) {
|
||||
if (is.null(rohwert) || length(rohwert) == 0) return(list(wert = NA_real_, status = "fehlt"))
|
||||
wert_roh = rohwert[1]
|
||||
if (is.na(wert_roh)) return(list(wert = NA_real_, status = "fehlt"))
|
||||
if (inherits(wert_roh, "haven_labelled")) wert_roh = haven::zap_labels(wert_roh)
|
||||
wert = suppressWarnings(as.numeric(wert_roh))
|
||||
if (is.na(wert) || wert < 1 || wert > 10) return(list(wert = NA_real_, status = "ungueltig"))
|
||||
list(wert = wert, status = "ok")
|
||||
}
|
||||
|
||||
# UNVERIFIZIERTE ANNAHME (siehe Abschnitt 4, Punkt 2 der Spezifikation): Es ist nicht durch
|
||||
# einen echten formr-Testdurchlauf bestaetigt, in welchem Format check-Items exportiert werden
|
||||
# (TRUE/FALSE, 1/0/NA oder Zeichenkette). Alle plausiblen Formen werden abgefangen.
|
||||
ist_angekreuzt = function(x) {
|
||||
if (is.null(x) || length(x) == 0) return(FALSE)
|
||||
wert = x[1]
|
||||
if (is.na(wert)) return(FALSE)
|
||||
wert_norm = tolower(trimws(as.character(wert)))
|
||||
wert_norm %in% c("true", "1")
|
||||
}
|
||||
|
||||
farbverlauf_funktion = colorRampPalette(c(FARBE_GUT, FARBE_SCHLECHT))
|
||||
|
||||
farbe_funktionsniveau = function(wert, min_wert, max_wert) {
|
||||
if (is.null(wert) || length(wert) == 0 || is.na(wert)) return("#CCCCCC")
|
||||
frac = (wert - min_wert) / (max_wert - min_wert)
|
||||
frac = max(0, min(1, frac))
|
||||
farbverlauf_funktion(101)[round(frac * 100) + 1]
|
||||
}
|
||||
|
||||
make_profil_plot = function(df) {
|
||||
ggplot(df, aes(x = reorder(label, -kat_nr), y = summe, fill = summe)) +
|
||||
geom_col(width = 0.6, na.rm = TRUE) +
|
||||
coord_flip(clip = "off") +
|
||||
scale_fill_gradient(low = FARBE_GUT, high = FARBE_SCHLECHT, limits = c(0, 20), guide = "none") +
|
||||
scale_y_continuous(limits = c(0, 20), breaks = seq(0, 20, 5),
|
||||
expand = expansion(mult = c(0, 0.18))) +
|
||||
geom_text(aes(label = beschriftung), hjust = -0.05, size = 3.3, color = "#333333") +
|
||||
labs(x = NULL, y = "Summenwert je Kategorie (0-20, hoch = schlechteres Funktionieren)") +
|
||||
theme_minimal(base_size = 12) +
|
||||
theme(
|
||||
panel.grid.major.y = element_blank(),
|
||||
panel.grid.minor = element_blank(),
|
||||
plot.margin = margin(t = 5, r = 60, b = 5, l = 5)
|
||||
)
|
||||
}
|
||||
|
||||
make_kat10_plot = function(wert) {
|
||||
df = data.frame(x = "Kategorie 10", y = wert)
|
||||
ggplot(df, aes(x = x, y = y, fill = y)) +
|
||||
geom_col(width = 0.5, na.rm = TRUE) +
|
||||
coord_flip(clip = "off", ylim = c(1, 10)) +
|
||||
scale_fill_gradient(low = FARBE_GUT, high = FARBE_SCHLECHT, limits = c(1, 10), guide = "none") +
|
||||
scale_y_continuous(breaks = 1:10,
|
||||
expand = expansion(mult = c(0, 0.12))) +
|
||||
geom_text(aes(label = y), hjust = -0.3, size = 4.2, fontface = "bold", color = "#333333") +
|
||||
labs(x = NULL, y = "Kategorie 10: Allgemeine Funktionsfaehigkeit (1 = gut, 10 = schlecht)") +
|
||||
theme_minimal(base_size = 12) +
|
||||
theme(
|
||||
axis.text.y = element_blank(),
|
||||
axis.ticks.y = element_blank(),
|
||||
panel.grid.major.y = element_blank(),
|
||||
panel.grid.minor = element_blank(),
|
||||
plot.margin = margin(t = 5, r = 40, b = 5, l = 5)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
# Datenaufbereitung ####
|
||||
|
||||
# Feste Item-Referenztabelle Teil 1 (19 Items, 10 Kategorien). Wird nicht zur Laufzeit aus
|
||||
# den Daten abgeleitet, siehe Abschnitt 2 der Spezifikation.
|
||||
FUNKTIONSNIVEAU_KAT_NAMEN = c(
|
||||
"1" = "1. Ordnen",
|
||||
"2" = "2. Anfangen",
|
||||
"3" = "3. Umsetzen",
|
||||
"4" = "4. Einteilen",
|
||||
"5" = "5. Planen",
|
||||
"6" = "6. Erkennen / Entnehmen",
|
||||
"7" = "7. Gedaechtnis",
|
||||
"8" = "8. Soziales",
|
||||
"9" = "9. Sonstiges"
|
||||
)
|
||||
|
||||
FUNKTIONSNIVEAU_KAT_ITEMS = list(
|
||||
"1" = c("funktionsniveau_kat01_a", "funktionsniveau_kat01_b"),
|
||||
"2" = c("funktionsniveau_kat02_a", "funktionsniveau_kat02_b"),
|
||||
"3" = c("funktionsniveau_kat03_a", "funktionsniveau_kat03_b"),
|
||||
"4" = c("funktionsniveau_kat04_a", "funktionsniveau_kat04_b"),
|
||||
"5" = c("funktionsniveau_kat05_a", "funktionsniveau_kat05_b"),
|
||||
"6" = c("funktionsniveau_kat06_a", "funktionsniveau_kat06_b"),
|
||||
"7" = c("funktionsniveau_kat07_a", "funktionsniveau_kat07_b"),
|
||||
"8" = c("funktionsniveau_kat08_a", "funktionsniveau_kat08_b"),
|
||||
"9" = c("funktionsniveau_kat09_a", "funktionsniveau_kat09_b")
|
||||
)
|
||||
|
||||
FUNKTIONSNIVEAU_ITEM_TEXTE = c(
|
||||
funktionsniveau_kat01_a = "Wenn ich zwischen verschiedenen Moeglichkeiten entscheiden muss",
|
||||
funktionsniveau_kat01_b = "Wenn viele Dinge gleichzeitig zu tun sind",
|
||||
funktionsniveau_kat02_a = "Wenn ein laengerfristiges Vorhaben begonnen werden soll",
|
||||
funktionsniveau_kat02_b = "Wenn der erste Schritt bei einer Arbeit / einem Projekt gemacht werden soll",
|
||||
funktionsniveau_kat03_a = "Wenn eine Aufgabe puenktlich und wie verabredet erledigt werden soll",
|
||||
funktionsniveau_kat03_b = "Wenn Alltagsroutinen erledigt werden sollen (z.B. Aufraeumen, Einkaufen)",
|
||||
funktionsniveau_kat04_a = "Wenn es um Geld geht (z.B. Einteilen)",
|
||||
funktionsniveau_kat04_b = "Wenn es um den Tagesablauf und die Arbeitszeit geht",
|
||||
funktionsniveau_kat05_a = "Wenn etwas im Voraus geplant werden soll",
|
||||
funktionsniveau_kat05_b = "Wenn ein Schritt-fuer-Schritt-Vorgehen notwendig ist",
|
||||
funktionsniveau_kat06_a = "Wenn ich laenger zuhoeren und das Wesentliche verstehen will (z.B. im Gespraech)",
|
||||
funktionsniveau_kat06_b = "Wenn ich mich orientieren muss (z.B. in einer Stadt, in Schule oder Beruf)",
|
||||
funktionsniveau_kat07_a = "Wenn ich mir etwas merken will (z.B. Name, Zahlen, Termine)",
|
||||
funktionsniveau_kat07_b = "Wenn ich mich an etwas erinnern soll (z.B. Termin, Absprache, Aufgaben)",
|
||||
funktionsniveau_kat08_a = "Wenn ich in einer Gruppe bin (z.B. Freunde, Arbeitsgruppe)",
|
||||
funktionsniveau_kat08_b = "Wenn andere nicht mit mir uebereinstimmen, d.h. anderer Meinung sind",
|
||||
funktionsniveau_kat09_a = "Gelingt mir ... (Fortsetzung im Freitext)",
|
||||
funktionsniveau_kat09_b = "Gelingt mir ... (Fortsetzung im Freitext)",
|
||||
funktionsniveau_kat10 = "Meine Leistungsfaehigkeit und geistige Gesundheit sind (gut - schlecht)"
|
||||
)
|
||||
|
||||
# Feste Item-Referenztabelle Teil 2 (116 Items, Zusatzfragen-Checkliste ohne Score),
|
||||
# siehe Abschnitt 3 der Spezifikation.
|
||||
ZUSATZFRAGEN_TEXTE = c(
|
||||
zusatzfragen_001 = "Ich bin Links- oder Beidhaender.",
|
||||
zusatzfragen_002 = "In meiner Familie gibt es Faelle von Drogen-/Alkoholmissbrauch, Depressionen oder manisch-depressiven Leiden.",
|
||||
zusatzfragen_003 = "Ich leide unter Stimmungsschwankungen.",
|
||||
zusatzfragen_004 = "Ich galt in der Schule oder gelte heute als leistungsschwach.",
|
||||
zusatzfragen_005 = "Ich habe Schwierigkeiten, Dinge anzufangen.",
|
||||
zusatzfragen_006 = "Ich trommle haeufig mit den Fingern, klopfe mit den Fuessen auf den Boden, zapple herum oder kann nicht still sitzen.",
|
||||
zusatzfragen_007 = "Ich bin sehr temperatur- oder geraeuschempfindlich.",
|
||||
zusatzfragen_008 = "Ich muss haeufig einen Absatz oder eine ganze Seite noch einmal lesen, weil ich mit offenen Augen traeume.",
|
||||
zusatzfragen_009 = "Ich habe haeufig Absencen oder Trancezustaende.",
|
||||
zusatzfragen_010 = "Meine Mutter hat waehrend der Schwangerschaft geraucht.",
|
||||
zusatzfragen_011 = "Es faellt mir schwer, mich zu entspannen.",
|
||||
zusatzfragen_012 = "Ich konnte mich als Kind nicht alleine beschaeftigen.",
|
||||
zusatzfragen_013 = "Ich bin extrem ungeduldig.",
|
||||
zusatzfragen_014 = "Ich konnte/kann nicht verlieren.",
|
||||
zusatzfragen_015 = "Ich musste als Kind unbedingt meinen Willen durchsetzen.",
|
||||
zusatzfragen_016 = "Meine Leistungsfaehigkeit schwankt extrem.",
|
||||
zusatzfragen_017 = "Ich fange viele Unternehmungen gleichzeitig an, sodass ich oft mehr Baelle in der Luft habe als ich bewaeltigen kann.",
|
||||
zusatzfragen_018 = "Ich bin impulsiv.",
|
||||
zusatzfragen_019 = "Ich hielt mich als Kind nicht an Regeln.",
|
||||
zusatzfragen_020 = "Ich habe haeufiger die Schule geschwaenzt.",
|
||||
zusatzfragen_021 = "Meine Konzentrationsfaehigkeit laesst leicht nach.",
|
||||
zusatzfragen_022 = "Wenn mich etwas wirklich interessiert, habe ich eine extrem gute Konzentrationsfaehigkeit, obwohl sie sonst leicht nachlaesst.",
|
||||
zusatzfragen_023 = "Ich schiebe staendig Dinge auf die lange Bank.",
|
||||
zusatzfragen_024 = "Ich bin haeufig Feuer und Flamme fuer ein Projekt und bleibe dann doch nicht an der Sache dran.",
|
||||
zusatzfragen_025 = "Ich bin leicht ablenkbar durch Unwichtiges.",
|
||||
zusatzfragen_026 = "Ich bin im Besitz einer Fahrerlaubnis. Diese wurde mir schon einmal entzogen, ich wurde bereits mehrfach wegen erhoehter Geschwindigkeit oder schon einmal wegen Fahrens ohne Fahrerlaubnis belangt.",
|
||||
zusatzfragen_027 = "Ich war/bin motorisch sehr ungeschickt, d.h. ich stosse haeufig an, stolpere, ziehe mit Verletzungen zu etc.",
|
||||
zusatzfragen_028 = "Es faellt mir schwerer als den meisten Menschen, mich verstaendlich zu machen.",
|
||||
zusatzfragen_029 = "Mein Gedaechtnis ist so loechrig, dass ich auf dem Weg von einem Zimmer ins andere manchmal vergesse, was ich dort holen wollte.",
|
||||
zusatzfragen_030 = "Ich bin Raucher.",
|
||||
zusatzfragen_031 = "In meiner Freizeit uebe ich einen Extremsport aus (z.B. Gleitschirmfliegen, Bungee Jumping, Motorsport).",
|
||||
zusatzfragen_032 = "Ich habe mindestens eine Klasse wiederholt.",
|
||||
zusatzfragen_033 = "Ich zerstoerte als Kind oefter mutwillig Spielsachen oder Gegenstaende.",
|
||||
zusatzfragen_034 = "Ich trinke zu viel Alkohol.",
|
||||
zusatzfragen_035 = "Ich bin in Kindheit/Jugend mit Stehlen oder Zuendeln aufgefallen.",
|
||||
zusatzfragen_036 = "Kokain/Amphetamin macht mich nicht high, sondern ruhiger und verbessert meine Konzentrationsfaehigkeit.",
|
||||
zusatzfragen_037 = "Ich litt unter Bettnaessen.",
|
||||
zusatzfragen_038 = "Ich zappe in Radio/Fernsehen haeufig umher.",
|
||||
zusatzfragen_039 = "Ich fuehle mich getrieben, als ob in meinem Inneren staendig ein Motor liefe.",
|
||||
zusatzfragen_040 = 'Als Kind wurde ich z.B. "faul", "impulsiv", "aggressiv" oder einfach "boese" genannt.',
|
||||
zusatzfragen_041 = "Meine engeren Beziehungen sind belastet durch meine Unfaehigkeit, mich ueber laengere Zeit an einem Gespraech zu beteiligen.",
|
||||
zusatzfragen_042 = "Ich bin immer in Bewegung, auch wenn ich es gar nicht moechte.",
|
||||
zusatzfragen_043 = "Ich kann schlechter als andere Menschen warten, bis ich an der Reihe bin.",
|
||||
zusatzfragen_044 = "Ich bin nicht in der Lage, zuerst die Gebrauchsanweisung zu lesen, bevor ich anfange.",
|
||||
zusatzfragen_045 = "Ich bin eine Mimose.",
|
||||
zusatzfragen_046 = "Ich treibe exzessiv Sport.",
|
||||
zusatzfragen_047 = "Ich muss dauernd aufpassen, dass ich nicht mit etwas Falschem herausplatze.",
|
||||
zusatzfragen_048 = "Ich war/bin Schlafwandler.",
|
||||
zusatzfragen_049 = "Ich liebe Gluecksspiel.",
|
||||
zusatzfragen_050 = "Ich habe das Gefuehl, innerlich in die Luft zu gehen, wenn jemand Muehe hat, zur Sache zu kommen.",
|
||||
zusatzfragen_051 = "Ich war als Kind motorisch sehr unruhig (hyperaktiv).",
|
||||
zusatzfragen_052 = "Spannende Situationen mit ziehen mich magisch an.",
|
||||
zusatzfragen_053 = "Ich versuche haeufig, eher die Dinge zu tun, die mir schwer fallen, als die, die mir leicht fallen.",
|
||||
zusatzfragen_054 = 'Ich handle ueberwiegend "aus dem Bauch heraus" (intuitiv).',
|
||||
zusatzfragen_055 = "Ich gerate haeufig in eine Lage, in die ich gar nicht geraten will.",
|
||||
zusatzfragen_056 = "Ich wuerde mich lieber einer Wurzelbehandlung beim Zahnarzt unterziehen als nach Plan vorzugehen.",
|
||||
zusatzfragen_057 = "Ich beschliesse dauernd, mein Leben besser zu organisieren, nur um festzustellen, dass ich mich staendig am Rande des Chaos befinde.",
|
||||
zusatzfragen_058 = 'Ich habe haeufig einen Juckreiz, den ich nicht beseitigen kann, oder Appetit auf "mehr von irgendetwas", und ich weiss nicht wovon.',
|
||||
zusatzfragen_059 = "Ich bin uebersexualisiert, d.h. ich denke z.B. sehr viel an Sex oder interessiere mich auffallend fuer Themen wie Prostitution.",
|
||||
zusatzfragen_060 = "Ich neige zu Suchtverhalten.",
|
||||
zusatzfragen_061 = "Ich kann einen Menschen verstehen, der die ungewoehnliche Symptomentrias Kokainmissbrauch, haeufige Lektuere pornografischer Schriften und Sucht nach Kreuzwortraetseln aufweist, auch wenn ich selbst diese Symptome nicht habe.",
|
||||
zusatzfragen_062 = "Ich flirte mehr als ich eigentlich will.",
|
||||
zusatzfragen_063 = "Ich bin in einer chaotischen, undisziplinierten Familie aufgewachsen.",
|
||||
zusatzfragen_064 = "Es faellt mir schwer, alleine zu sein.",
|
||||
zusatzfragen_065 = "Ich bekaempfe depressive Verstimmungen haeufig durch irgendwelche moeglicherweise schaedlichen zwanghaften Verhaltensweisen (z.B. zu viel Arbeit, unkontrollierte Geldausgaben, zu viel Essen oder Trinken).",
|
||||
zusatzfragen_066 = "Ich habe eine Lese-Rechtschreibschwaeche (Legasthenie) oder eine Rechenschwaeche (Dyskalkulie).",
|
||||
zusatzfragen_067 = 'Bei einem Verwandten ersten Grades (z.B. Eltern, Geschwister, Kinder) wurde schon einmal "Aktivitaets- und Aufmerksamkeitsstoerung" (ADS) oder "Hyperaktivitaet" (ADHS) diagnostiziert.',
|
||||
zusatzfragen_068 = "Ich habe grosse Probleme, Frustrationen zu ertragen.",
|
||||
zusatzfragen_069 = 'Ich bin unruhig, wenn es in meinem Leben keine "Action" gibt.',
|
||||
zusatzfragen_070 = "Ich habe Muehe, ein Buch ganz zu lesen.",
|
||||
zusatzfragen_071 = "Ich kann besser mit den Folgen von Regelverstoessen leben als mit der Frustration, die das Befolgen von Vorschriften und Regeln fuer mich bedeuten wuerde.",
|
||||
zusatzfragen_072 = "Ich habe viele irrationale Aengste.",
|
||||
zusatzfragen_073 = "Ich bringe oft Buchstaben in Woertern oder Ziffern in Zahlen durcheinander.",
|
||||
zusatzfragen_074 = "Ich habe als Autofahrer schon mehr als vier Unfaelle verschuldet.",
|
||||
zusatzfragen_075 = "Ich kann schlecht mit Geld umgehen.",
|
||||
zusatzfragen_076 = "Ich bin der stuermische, dynamische Typ.",
|
||||
zusatzfragen_077 = "In meinem Leben gibt es nur wenig Strukturiertheit und Schematismus, obwohl beides eine beruhigende Wirkung auf mich ausuebt.",
|
||||
zusatzfragen_078 = "Ich bin oefter als einmal geschieden.",
|
||||
zusatzfragen_079 = "Ich habe Selbstwertprobleme.",
|
||||
zusatzfragen_080 = "Ich habe eine schlechte visuomotorische Koordination, d.h. ich kann die durch Sehen aufgenommene Informationen (Input) schlecht mit der Motorik (Output) in Einklang bringen.",
|
||||
zusatzfragen_081 = "Ich war als Kind sehr unsportlich.",
|
||||
zusatzfragen_082 = "Ich habe haeufig meinen Arbeitsplatz gekuendigt oder bin gekuendigt worden.",
|
||||
zusatzfragen_083 = "Ich bin Einzelgaenger.",
|
||||
zusatzfragen_084 = "Ich finde es fast unmoeglich, ein Adressbuch oder eine Adresskartei zu fuehren.",
|
||||
zusatzfragen_085 = "Ich bin an einem Tag die Stimmungskanone auf einer Party und schaeme mich am naechsten Tag dafuer.",
|
||||
zusatzfragen_086 = "Unerwartet freie Zeit weiss ich haeufig nicht sinnvoll zu nutzen oder fuehle mich dann deprimiert.",
|
||||
zusatzfragen_087 = "Ich bin kreativer und phantasievoller als die meisten Menschen.",
|
||||
zusatzfragen_088 = "Ich habe staendig Probleme, aufmerksam zu sein oder bei der Stange zu bleiben.",
|
||||
zusatzfragen_089 = "Ich arbeite am besten in kurzen Schueben.",
|
||||
zusatzfragen_090 = "Ich ueberziehe regelmaessig mein Konto.",
|
||||
zusatzfragen_091 = "Ich will unbedingt immer etwas Neues ausprobieren.",
|
||||
zusatzfragen_092 = "Nach einem Erfolg bin ich haeufig deprimiert.",
|
||||
zusatzfragen_093 = 'Ich suche haeufig nach dem "Nonplusultra".',
|
||||
zusatzfragen_094 = "Ich habe das Gefuehl, haeufig hinter meinen Moeglichkeiten zurueckzubleiben.",
|
||||
zusatzfragen_095 = "Ich fuehle mich besonders ratlos.",
|
||||
zusatzfragen_096 = 'Ich war in der Schule ein "Tagtraeumer" bzw. "Hans-guck-in-die-Luft".',
|
||||
zusatzfragen_097 = 'Ich war manchmal der "Klassenclown".',
|
||||
zusatzfragen_098 = 'Ich wurde schon einmal als "gierig" oder "unersaettlich" bezeichnet.',
|
||||
zusatzfragen_099 = "Ich kann meine Wirkung auf andere Menschen nur sehr schwer einschaetzen.",
|
||||
zusatzfragen_100 = "Ich gehe Probleme meist intuitiv an.",
|
||||
zusatzfragen_101 = "Ich suche einen Weg lieber nach Gefuehl als eine Karte zur Orientierung zu benutzen.",
|
||||
zusatzfragen_102 = "Ich bin beim Geschlechtsverkehr haeufig abgelenkt, obwohl ich Sex mag.",
|
||||
zusatzfragen_103 = "Ich bin ein Adoptivkind.",
|
||||
zusatzfragen_104 = "Ich leide unter Neurodermitis, Allergien oder Asthma.",
|
||||
zusatzfragen_105 = "Ich hatte als Kind haeufig Ohrinfektionen.",
|
||||
zusatzfragen_106 = "Ich arbeite effizienter, wenn ich mein eigener Chef bin.",
|
||||
zusatzfragen_107 = "Ich bin intelligenter als ich mich praesentieren kann.",
|
||||
zusatzfragen_108 = "Ich bin besonders unsicher.",
|
||||
zusatzfragen_109 = "Ich kann Geheimnisse nur schwer fuer mich behalten.",
|
||||
zusatzfragen_110 = "Ich vergesse haeufig, was ich sagen will, in dem Moment, wo ich es sagen will.",
|
||||
zusatzfragen_111 = "Ich reise gerne.",
|
||||
zusatzfragen_112 = "Ich leide an Platzangst (Klaustrophobie).",
|
||||
zusatzfragen_113 = "Ich habe mich schon einmal gefragt, ob ich verrueckt bin.",
|
||||
zusatzfragen_114 = "Ich erfasse schnell den springenden Punkt einer Sache.",
|
||||
zusatzfragen_115 = "Ich lache viel.",
|
||||
zusatzfragen_116 = "Es bereite mir Schwierigkeiten, meine Aufmerksamkeit so lange auf diesen Fragebogen zu konzentrieren, dass ich ihn zu Ende lesen konnte."
|
||||
)
|
||||
|
||||
berechne_funktionsniveau_profil = function(zeile) {
|
||||
kategorien = lapply(names(FUNKTIONSNIVEAU_KAT_ITEMS), function(k) {
|
||||
item_namen = FUNKTIONSNIVEAU_KAT_ITEMS[[k]]
|
||||
einzelwerte = lapply(item_namen, function(nm) {
|
||||
rohwert = if (nm %in% names(zeile)) zeile[[nm]][1] else NULL
|
||||
hole_range_wert(rohwert)
|
||||
})
|
||||
gueltige = sapply(einzelwerte, function(e) if (e$status == "ok") e$wert else NA_real_)
|
||||
n_items = sum(!is.na(gueltige))
|
||||
summe = if (n_items > 0) sum(gueltige, na.rm = TRUE) else NA_real_
|
||||
n_ungueltig = sum(sapply(einzelwerte, function(e) e$status == "ungueltig"))
|
||||
|
||||
list(
|
||||
kat_nr = as.integer(k),
|
||||
label = FUNKTIONSNIVEAU_KAT_NAMEN[[k]],
|
||||
summe = summe,
|
||||
n_items = n_items,
|
||||
n_erwartet = length(item_namen),
|
||||
n_ungueltig = n_ungueltig,
|
||||
anzeigen = n_items > 0
|
||||
)
|
||||
})
|
||||
names(kategorien) = names(FUNKTIONSNIVEAU_KAT_ITEMS)
|
||||
|
||||
kat10_roh = if ("funktionsniveau_kat10" %in% names(zeile)) zeile[["funktionsniveau_kat10"]][1] else NULL
|
||||
kat10_werte = hole_range_wert(kat10_roh)
|
||||
|
||||
kat09_a_text = if ("funktionsniveau_kat09_a_text" %in% names(zeile))
|
||||
as.character(zeile[["funktionsniveau_kat09_a_text"]][1]) else NA_character_
|
||||
kat09_b_text = if ("funktionsniveau_kat09_b_text" %in% names(zeile))
|
||||
as.character(zeile[["funktionsniveau_kat09_b_text"]][1]) else NA_character_
|
||||
if (!is.na(kat09_a_text) && trimws(kat09_a_text) == "") kat09_a_text = NA_character_
|
||||
if (!is.na(kat09_b_text) && trimws(kat09_b_text) == "") kat09_b_text = NA_character_
|
||||
|
||||
list(
|
||||
kategorien = kategorien,
|
||||
kat10 = kat10_werte,
|
||||
kat09_texte = list(a = kat09_a_text, b = kat09_b_text)
|
||||
)
|
||||
}
|
||||
|
||||
kategorien_zu_df = function(kategorien) {
|
||||
zeilen = lapply(kategorien, function(k) {
|
||||
if (!k$anzeigen) return(NULL)
|
||||
beschriftung = if (k$n_items < k$n_erwartet) {
|
||||
paste0(k$summe, " (", k$n_items, " von ", k$n_erwartet, " Items)")
|
||||
} else {
|
||||
as.character(k$summe)
|
||||
}
|
||||
data.frame(
|
||||
kat_nr = k$kat_nr, label = k$label, summe = k$summe,
|
||||
beschriftung = beschriftung, n_ungueltig = k$n_ungueltig,
|
||||
n_items = k$n_items, n_erwartet = k$n_erwartet,
|
||||
stringsAsFactors = FALSE
|
||||
)
|
||||
})
|
||||
zeilen = Filter(Negate(is.null), zeilen)
|
||||
if (length(zeilen) == 0) return(NULL)
|
||||
do.call(rbind, zeilen)
|
||||
}
|
||||
|
||||
extrahiere_zusatzfragen = function(zeile) {
|
||||
spalten = names(ZUSATZFRAGEN_TEXTE)
|
||||
treffer = Filter(function(nm) {
|
||||
if (!(nm %in% names(zeile))) return(FALSE)
|
||||
ist_angekreuzt(zeile[[nm]][1])
|
||||
}, spalten)
|
||||
if (length(treffer) == 0)
|
||||
return(data.frame(item = character(0), nr = integer(0), text = character(0),
|
||||
stringsAsFactors = FALSE))
|
||||
nrs = as.integer(sub("zusatzfragen_", "", treffer))
|
||||
df = data.frame(
|
||||
item = treffer,
|
||||
nr = nrs,
|
||||
text = unname(ZUSATZFRAGEN_TEXTE[treffer]),
|
||||
stringsAsFactors = FALSE
|
||||
)
|
||||
df[order(df$nr), ]
|
||||
}
|
||||
|
||||
|
||||
# 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; cursor: pointer;
|
||||
}
|
||||
.btn-laden:hover { background: #6d1e29 !important; }
|
||||
#download_word {
|
||||
background: #8B2635; color: white; border: none;
|
||||
border-radius: 4px; padding: 8px 20px; font-weight: 600;
|
||||
}
|
||||
#download_word:hover { background: #6d1e29; color: white; }
|
||||
.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: 12px; 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; }
|
||||
.richtungs-hinweis {
|
||||
background: #FFEBEE; border-left: 5px solid #B71C1C;
|
||||
padding: 10px 16px; border-radius: 4px; color: #B71C1C;
|
||||
margin-bottom: 14px; font-size: 0.93em; font-weight: 700;
|
||||
}
|
||||
.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: 32px; flex-shrink: 0; }
|
||||
.item-text { flex: 1; color: #333; font-size: 0.92em; }
|
||||
.kat-ungueltig-hinweis { font-size: 0.82em; color: #BF360C; margin: -6px 0 8px 4px; }
|
||||
"
|
||||
|
||||
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("ADHS-Funktionsniveau"),
|
||||
tags$p("Funktionsniveau-Skala (10 Kategorien, 19 Items) und Zusatzfragen-Checkliste (116 Items)")
|
||||
),
|
||||
|
||||
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_adhs_funktionsniveau_docx = function(d) {
|
||||
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_hinweis = fp_text(bold = TRUE, font.size = 10.5, color = "#B71C1C")
|
||||
fp_disclaimer = fp_text(font.size = 9, italic = TRUE, color = "#777777")
|
||||
|
||||
doc = body_add_fpar(doc, fpar(ftext("ADHS-Funktionsniveau - Einzelauswertung", fp_titel)))
|
||||
doc = body_add_fpar(doc, fpar(
|
||||
ftext("Chiffre: ", fp_label), ftext(d$chiffre, fp_normal),
|
||||
ftext(" Ausfuelldatum: ", fp_label), ftext(d$datum_str, fp_normal)
|
||||
))
|
||||
if (!is.null(d$ausfuelldatum_hinweis)) {
|
||||
doc = body_add_fpar(doc, fpar(ftext(d$ausfuelldatum_hinweis,
|
||||
fp_text(font.size = 9, italic = TRUE, color = "#BF360C"))))
|
||||
}
|
||||
if (!is.null(d$info_mehrere)) {
|
||||
doc = body_add_fpar(doc, fpar(ftext(d$info_mehrere,
|
||||
fp_text(font.size = 10, italic = TRUE, color = "#555555"))))
|
||||
}
|
||||
doc = body_add_par(doc, "", style = "Normal")
|
||||
|
||||
doc = body_add_fpar(doc, fpar(ftext(ADHS_FUNKTIONSNIVEAU_RICHTUNGSHINWEIS, fp_hinweis)))
|
||||
doc = body_add_par(doc, "", style = "Normal")
|
||||
|
||||
doc = body_add_fpar(doc, fpar(ftext("Funktionsniveau-Profil (Kategorien 1-9, Skala 0-20)", fp_abschnitt)))
|
||||
for (k in d$kategorien) {
|
||||
if (!k$anzeigen) {
|
||||
doc = body_add_fpar(doc, fpar(
|
||||
ftext(paste0(k$label, ": "), fp_label),
|
||||
ftext("nicht ausgefuellt", fp_normal)
|
||||
))
|
||||
next
|
||||
}
|
||||
farbe = farbe_funktionsniveau(k$summe, 0, 20)
|
||||
fp_wert = fp_text(bold = TRUE, font.size = 11, color = "#FFFFFF", shading.color = farbe)
|
||||
zusatz_txt = if (k$n_items < k$n_erwartet)
|
||||
paste0(" (basiert auf ", k$n_items, " von ", k$n_erwartet, " moeglichen Items)") else ""
|
||||
doc = body_add_fpar(doc, fpar(
|
||||
ftext(paste0(k$label, ": "), fp_label),
|
||||
ftext(paste0(" ", k$summe, " ", zusatz_txt, " "), fp_wert)
|
||||
))
|
||||
if (k$n_ungueltig > 0) {
|
||||
doc = body_add_fpar(doc, fpar(ftext(
|
||||
paste0(" Hinweis: ", k$n_ungueltig, " Item(e) nicht auswertbar, in der Summe nicht beruecksichtigt."),
|
||||
fp_text(font.size = 9, italic = TRUE, color = "#BF360C"))))
|
||||
}
|
||||
}
|
||||
doc = body_add_par(doc, "", style = "Normal")
|
||||
|
||||
doc = body_add_fpar(doc, fpar(ftext("Kategorie 10: Allgemeine Funktionsfaehigkeit (Skala 1-10)", fp_abschnitt)))
|
||||
if (d$kat10$status == "ok") {
|
||||
farbe10 = farbe_funktionsniveau(d$kat10$wert, 1, 10)
|
||||
doc = body_add_fpar(doc, fpar(
|
||||
ftext(paste0(" ", d$kat10$wert, " / 10 "),
|
||||
fp_text(bold = TRUE, font.size = 12, color = "#FFFFFF", shading.color = farbe10))
|
||||
))
|
||||
} else if (d$kat10$status == "ungueltig") {
|
||||
doc = body_add_fpar(doc, fpar(ftext("Wert nicht auswertbar.", fp_normal)))
|
||||
} else {
|
||||
doc = body_add_fpar(doc, fpar(ftext("Nicht beantwortet.", fp_normal)))
|
||||
}
|
||||
doc = body_add_par(doc, "", style = "Normal")
|
||||
|
||||
if (!is.na(d$kat09_texte$a) || !is.na(d$kat09_texte$b)) {
|
||||
doc = body_add_fpar(doc, fpar(ftext("Freitext zu Kategorie 9 (Sonstiges)", fp_abschnitt)))
|
||||
if (!is.na(d$kat09_texte$a))
|
||||
doc = body_add_fpar(doc, fpar(ftext("a: ", fp_label), ftext(d$kat09_texte$a, fp_normal)))
|
||||
if (!is.na(d$kat09_texte$b))
|
||||
doc = body_add_fpar(doc, fpar(ftext("b: ", fp_label), ftext(d$kat09_texte$b, fp_normal)))
|
||||
doc = body_add_par(doc, "", style = "Normal")
|
||||
}
|
||||
|
||||
doc = body_add_fpar(doc, fpar(ftext("Zusatzfragen (Teil 2, ohne Score)", fp_abschnitt)))
|
||||
if (nrow(d$zusatzfragen) == 0) {
|
||||
doc = body_add_fpar(doc, fpar(ftext("Keine Zusatzfragen angekreuzt.", fp_normal)))
|
||||
} else {
|
||||
for (i in seq_len(nrow(d$zusatzfragen))) {
|
||||
row = d$zusatzfragen[i, ]
|
||||
doc = body_add_fpar(doc, fpar(ftext(paste0(row$nr, ". ", row$text), fp_normal)))
|
||||
}
|
||||
}
|
||||
doc = body_add_par(doc, "", style = "Normal")
|
||||
|
||||
doc = body_add_fpar(doc, fpar(ftext(ADHS_FUNKTIONSNIVEAU_DISCLAIMER, fp_disclaimer)))
|
||||
|
||||
doc
|
||||
}
|
||||
|
||||
|
||||
# Server ####
|
||||
|
||||
server = function(input, output, session) {
|
||||
|
||||
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)))
|
||||
}
|
||||
})
|
||||
|
||||
# Skripte werden NICHT beim App-Start gesourct, nur beim Klick.
|
||||
ergebnis_r = eventReactive(input$btn_suchen, {
|
||||
|
||||
chiffre = toupper(trimws(input$chiffre))
|
||||
|
||||
if (nchar(trimws(input$pseudonym)) == 0 && nchar(chiffre) == 0) {
|
||||
return(list(error = "Bitte Chiffre oder Pseudonym eingeben."))
|
||||
}
|
||||
if (nchar(trimws(input$pseudonym)) == 0 && !grepl("^[A-Z][0-9]{6}$", chiffre)) {
|
||||
return(list(error = "Ungueltige Chiffre. Erwartet: ein Grossbuchstabe + 6 Ziffern (z.B. P000123)."))
|
||||
}
|
||||
|
||||
if (!file.exists(PFAD_DOWNLOAD_SKRIPT))
|
||||
return(list(error = paste0("Download-Skript nicht gefunden:\n", PFAD_DOWNLOAD_SKRIPT)))
|
||||
if (!file.exists(PFAD_PSEUDONYM_SKRIPT))
|
||||
return(list(error = paste0("Pseudonym-Skript nicht gefunden:\n", PFAD_PSEUDONYM_SKRIPT)))
|
||||
|
||||
res_dl = tryCatch({
|
||||
source(PFAD_DOWNLOAD_SKRIPT, local = FALSE)
|
||||
list(ok = TRUE)
|
||||
}, error = function(e) list(ok = FALSE, msg = e$message))
|
||||
if (!res_dl$ok) return(list(error = paste0("Fehler im Download-Skript: ", res_dl$msg)))
|
||||
|
||||
if (!exists("daten_adhs_funktionsniveau", envir = .GlobalEnv))
|
||||
return(list(error = paste0(
|
||||
"Objekt 'daten_adhs_funktionsniveau' nach dem Sourcen nicht gefunden. ",
|
||||
"Bitte Download-Skript pruefen.")))
|
||||
|
||||
daten = get("daten_adhs_funktionsniveau", envir = .GlobalEnv)
|
||||
|
||||
# UNVERIFIZIERTE ANNAHME (siehe Abschnitt 4, Punkt 3 der Spezifikation): Name der
|
||||
# Session-Spalte. Vor dem ersten Produktiveinsatz mit einem echten Testdurchlauf pruefen.
|
||||
if (!("session" %in% names(daten)))
|
||||
return(list(error = "Erwartete Spalte 'session' nicht in den Daten gefunden - Datenexport pruefen."))
|
||||
|
||||
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()
|
||||
wd_ziel = if (!is.null(db_ordner)) db_ordner else
|
||||
dirname(normalizePath(PFAD_PSEUDONYM_SKRIPT, mustWork = FALSE))
|
||||
setwd(wd_ziel)
|
||||
on.exit(setwd(alter_wd), add = TRUE)
|
||||
|
||||
res_ps = tryCatch({
|
||||
source(PFAD_PSEUDONYM_SKRIPT, local = FALSE)
|
||||
list(ok = TRUE)
|
||||
}, error = function(e) list(ok = FALSE, msg = e$message))
|
||||
if (!res_ps$ok) return(list(error = paste0("Fehler im Pseudonym-Skript: ", res_ps$msg)))
|
||||
|
||||
if (!exists("pseudo", envir = .GlobalEnv))
|
||||
return(list(error = paste0(
|
||||
"Objekt 'pseudo' nach dem Sourcen nicht gefunden. Bitte Pseudonym-Skript pruefen.")))
|
||||
|
||||
pseudo_df = get("pseudo", envir = .GlobalEnv)
|
||||
|
||||
if (nchar(trimws(input$pseudonym)) > 0) {
|
||||
pw_treffer = pseudo_df[pseudo_df$pseudonym == trimws(input$pseudonym), ]
|
||||
if (nrow(pw_treffer) > 0) chiffre = toupper(trimws(pw_treffer$chiffre[1]))
|
||||
}
|
||||
|
||||
if (nchar(chiffre) == 0)
|
||||
return(list(error = "Pseudonym in der Datenbank nicht gefunden."))
|
||||
|
||||
treffer_ps = pseudo_df[pseudo_df$chiffre == chiffre, ]
|
||||
if (nrow(treffer_ps) == 0)
|
||||
return(list(error = 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(error = paste0(
|
||||
"Kein adhs_funktionsniveau-Datensatz fuer Chiffre '", chiffre, "' gefunden. (",
|
||||
length(alle_session_ids), " Pseudonym(e) geprueft)")))
|
||||
|
||||
info_mehrere = NULL
|
||||
if (nrow(treffer_dat) > 1) {
|
||||
n = nrow(treffer_dat)
|
||||
if ("created" %in% names(treffer_dat)) {
|
||||
treffer_dat = treffer_dat[order(treffer_dat$created, decreasing = TRUE), ]
|
||||
}
|
||||
info_mehrere = paste0(
|
||||
"Mehrere Ausfuellungen gefunden (", n, " Eintraege). Angezeigt wird die neueste."
|
||||
)
|
||||
treffer_dat = treffer_dat[1, , drop = FALSE]
|
||||
}
|
||||
|
||||
zeile = treffer_dat[1, , drop = FALSE]
|
||||
|
||||
# UNVERIFIZIERTE ANNAHME (siehe Abschnitt 4, Punkt 4 der Spezifikation): Quelle und
|
||||
# Format des Ausfuelldatums (Spalte 'created'). Vor dem ersten Produktiveinsatz mit
|
||||
# einem echten Testdurchlauf gegenpruefen.
|
||||
ausfuelldatum_hinweis = NULL
|
||||
datum_wert = if ("created" %in% names(zeile)) zeile[["created"]][1] else NA
|
||||
datum_obj = tryCatch({
|
||||
if (is.character(datum_wert)) as.Date(as.POSIXct(datum_wert)) else as.Date(datum_wert)
|
||||
}, error = function(e) NA)
|
||||
if (is.null(datum_obj) || length(datum_obj) == 0 || is.na(datum_obj)) {
|
||||
datum_obj = Sys.Date()
|
||||
ausfuelldatum_hinweis = "Ausfuelldatum aus den Daten nicht bestimmbar, Downloaddatum verwendet."
|
||||
}
|
||||
datum_str = format(datum_obj, "%d.%m.%Y")
|
||||
|
||||
profil = berechne_funktionsniveau_profil(zeile)
|
||||
zusatz = extrahiere_zusatzfragen(zeile)
|
||||
|
||||
list(
|
||||
error = NULL,
|
||||
chiffre = chiffre,
|
||||
datum_str = datum_str,
|
||||
datum_obj = datum_obj,
|
||||
info_mehrere = info_mehrere,
|
||||
ausfuelldatum_hinweis = ausfuelldatum_hinweis,
|
||||
kategorien = profil$kategorien,
|
||||
kat10 = profil$kat10,
|
||||
kat09_texte = profil$kat09_texte,
|
||||
zusatzfragen = zusatz
|
||||
)
|
||||
})
|
||||
|
||||
output$fehler_ui = renderUI({
|
||||
req(input$btn_suchen)
|
||||
d = ergebnis_r()
|
||||
if (!is.null(d$error)) div(class = "alert-fehler", d$error)
|
||||
})
|
||||
|
||||
output$warnung_ui = renderUI({
|
||||
req(input$btn_suchen)
|
||||
d = ergebnis_r()
|
||||
if (!is.null(d$error)) return(NULL)
|
||||
tagList(
|
||||
if (!is.null(d$info_mehrere)) div(class = "alert-warnung", d$info_mehrere),
|
||||
if (!is.null(d$ausfuelldatum_hinweis)) div(class = "alert-warnung", d$ausfuelldatum_hinweis)
|
||||
)
|
||||
})
|
||||
|
||||
output$ergebnis_ui = renderUI({
|
||||
req(input$btn_suchen)
|
||||
d = ergebnis_r()
|
||||
if (!is.null(d$error)) return(NULL)
|
||||
|
||||
df_profil = kategorien_zu_df(d$kategorien)
|
||||
|
||||
ungueltig_hinweise = lapply(d$kategorien, function(k) {
|
||||
if (k$n_ungueltig > 0) {
|
||||
div(class = "kat-ungueltig-hinweis",
|
||||
paste0(k$label, ": ", k$n_ungueltig, " Item(e) nicht auswertbar, nicht in der Summe beruecksichtigt."))
|
||||
}
|
||||
})
|
||||
|
||||
kat10_ui = if (d$kat10$status == "ok") {
|
||||
plotOutput("kat10_plot", height = "90px")
|
||||
} else if (d$kat10$status == "ungueltig") {
|
||||
div(class = "alert-warnung", "Kategorie 10 (Allgemeine Funktionsfaehigkeit): Wert nicht auswertbar.")
|
||||
} else {
|
||||
div(class = "alert-warnung", "Kategorie 10 (Allgemeine Funktionsfaehigkeit): nicht beantwortet.")
|
||||
}
|
||||
|
||||
kat09_ui = if (!is.na(d$kat09_texte$a) || !is.na(d$kat09_texte$b)) {
|
||||
tagList(
|
||||
tags$h5("Freitext zu Kategorie 9 (Sonstiges)"),
|
||||
if (!is.na(d$kat09_texte$a))
|
||||
div(class = "item-zeile", div(class = "item-nr", "a."), div(class = "item-text", d$kat09_texte$a)),
|
||||
if (!is.na(d$kat09_texte$b))
|
||||
div(class = "item-zeile", div(class = "item-nr", "b."), div(class = "item-text", d$kat09_texte$b))
|
||||
)
|
||||
}
|
||||
|
||||
zusatz_ui = if (nrow(d$zusatzfragen) == 0) {
|
||||
div(class = "alert-warnung", "Keine Zusatzfragen angekreuzt.")
|
||||
} else {
|
||||
lapply(seq_len(nrow(d$zusatzfragen)), function(i) {
|
||||
row = d$zusatzfragen[i, ]
|
||||
div(class = "item-zeile",
|
||||
div(class = "item-nr", paste0(row$nr, ".")),
|
||||
div(class = "item-text", row$text)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
tagList(
|
||||
div(class = "abschnitt-karte",
|
||||
div(class = "abschnitt-titel", "ADHS-Funktionsniveau"),
|
||||
|
||||
div(class = "meta-block",
|
||||
tags$strong("Chiffre: "), d$chiffre,
|
||||
tags$span(" | ", style = "color:#ccc;"),
|
||||
tags$strong("Ausfuelldatum: "), d$datum_str
|
||||
),
|
||||
|
||||
div(class = "richtungs-hinweis", ADHS_FUNKTIONSNIVEAU_RICHTUNGSHINWEIS),
|
||||
|
||||
tags$hr(),
|
||||
|
||||
tags$h5("Funktionsniveau-Profil (Kategorien 1-9)"),
|
||||
if (!is.null(df_profil))
|
||||
plotOutput("profil_plot", height = paste0(max(200, nrow(df_profil) * 38), "px"))
|
||||
else
|
||||
div(class = "alert-warnung", "Keine Kategorie konnte berechnet werden (keine gueltigen Werte)."),
|
||||
ungueltig_hinweise,
|
||||
|
||||
tags$hr(),
|
||||
|
||||
tags$h5("Kategorie 10: Allgemeine Funktionsfaehigkeit"),
|
||||
kat10_ui,
|
||||
|
||||
if (!is.null(kat09_ui)) tagList(tags$hr(), kat09_ui)
|
||||
),
|
||||
|
||||
div(class = "abschnitt-karte",
|
||||
div(class = "abschnitt-titel", "Zusatzfragen (Teil 2, ohne Score)"),
|
||||
div(zusatz_ui)
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
output$profil_plot = renderPlot({
|
||||
req(input$btn_suchen)
|
||||
d = ergebnis_r()
|
||||
req(is.null(d$error))
|
||||
df = kategorien_zu_df(d$kategorien)
|
||||
req(!is.null(df))
|
||||
make_profil_plot(df)
|
||||
}, bg = "transparent")
|
||||
|
||||
output$kat10_plot = renderPlot({
|
||||
req(input$btn_suchen)
|
||||
d = ergebnis_r()
|
||||
req(is.null(d$error))
|
||||
req(d$kat10$status == "ok")
|
||||
make_kat10_plot(d$kat10$wert)
|
||||
}, bg = "transparent")
|
||||
|
||||
output$download_word = downloadHandler(
|
||||
filename = function() {
|
||||
d = tryCatch(ergebnis_r(), error = function(e) NULL)
|
||||
chiffre = if (is.list(d) && is.null(d$error) && nchar(d$chiffre) > 0)
|
||||
d$chiffre else "export"
|
||||
datum = if (is.list(d) && is.null(d$error) && !is.null(d$datum_obj))
|
||||
format(d$datum_obj, "%Y%m%d") else format(Sys.Date(), "%Y%m%d")
|
||||
paste0("adhs_funktionsniveau_", chiffre, "_", datum, ".docx")
|
||||
},
|
||||
content = function(file) {
|
||||
d = tryCatch(ergebnis_r(), error = function(e) NULL)
|
||||
daten_ok = is.list(d) && is.null(d$error)
|
||||
if (!daten_ok) {
|
||||
doc = read_docx()
|
||||
doc = body_add_par(doc,
|
||||
"Kein Datensatz geladen. Bitte zuerst Chiffre oder Pseudonym eingeben und 'Auswerten' klicken.",
|
||||
style = "Normal")
|
||||
print(doc, target = file)
|
||||
return()
|
||||
}
|
||||
doc = tryCatch(
|
||||
erstelle_adhs_funktionsniveau_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)
|
||||
2879
ADHS-Funktionsniveau/renv.lock
Normal file
2879
ADHS-Funktionsniveau/renv.lock
Normal file
File diff suppressed because it is too large
Load diff
14
ADHS-Funktionsniveau/setup_renv.R
Normal file
14
ADHS-Funktionsniveau/setup_renv.R
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# Einmalig ausfuehren, bevor die App zum ersten Mal gestartet wird.
|
||||
# Initialisiert renv und installiert alle benoetigten Pakete.
|
||||
#
|
||||
# DBI und RSQLite werden vom gesourcten Pseudonym-Skript benoetigt,
|
||||
# nicht direkt von der App selbst.
|
||||
|
||||
renv::init()
|
||||
|
||||
pkgs = c("shiny", "dplyr", "ggplot2", "haven", "officer", "DBI", "RSQLite", "formr")
|
||||
install.packages(pkgs)
|
||||
|
||||
renv::snapshot()
|
||||
|
||||
message("Setup abgeschlossen. App starten mit: shiny::runApp()")
|
||||
Loading…
Add table
Add a link
Reference in a new issue