Initial commit

This commit is contained in:
Jonas Karneboge 2026-09-22 18:35:43 +02:00
commit 3cba772836
1341 changed files with 532924 additions and 0 deletions

BIN
STAI/.RData Normal file

Binary file not shown.

1
STAI/.Rprofile Normal file
View file

@ -0,0 +1 @@
source("renv/activate.R")

13
STAI/STAI.Rproj Normal file
View 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

911
STAI/app.R Normal file
View file

@ -0,0 +1,911 @@
# Präambel ####
PFAD_DOWNLOAD_SKRIPT = "../API/get_data_stai.R"
PFAD_PSEUDONYM_SKRIPT = "../get_pseudo.R"
PFAD_NORMTABELLEN = "normtabellen"
AKZENT_FARBE = "#8B2635"
STAI_DISCLAIMER = paste0(
"Diese Auswertung ist ein Hilfsmittel fuer klinisches Fachpersonal und ersetzt ",
"keine klinische Diagnose. Die Interpretation obliegt der behandelnden Person. ",
"Fuer die State-Skala existieren laut Testmanual keine Normtabellen; fuer die ",
"Trait-Skala ist kein fester klinischer Cutoff definiert."
)
# Verlauf gruen -> dunkelrot entspricht den 4 Antwortstufen 1-4 NACH Umpolung
# (Stufe = Ausmass der angezeigten Angst durch diese Antwort, nicht der Rohwert -
# bei Umkehr-Items zeigt sonst z.B. "sehr ruhig" faelschlich als dunkelrot an).
STAI_BADGE_FARBEN = c(
"1" = "#4CAF50",
"2" = "#F48FB1",
"3" = "#EF5350",
"4" = "#B71C1C"
)
STAI_BADGE_TEXT_FARBEN = c(
"1" = "white",
"2" = "#333333",
"3" = "white",
"4" = "white"
)
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)
PFAD_NORMTABELLEN = normalizePath(absPath(PFAD_NORMTABELLEN), mustWork = TRUE)
# Helper ####
# Entfernt Markdown-Reste aus formr-Itemtexten: fuehrende, escapete Itemnummer
# ("1\\. ...") und Fettschrift-Sternchen. Wird an jeder Stelle verwendet, an der
# Itemtext oder Introtext angezeigt wird (UI und Word-Export gleichermassen).
bereinige_markdown = function(text) {
x = as.character(text)
x = ifelse(is.na(x), "", x)
x = gsub("^\\d+\\\\\\.\\s*", "", x)
x = gsub("\\*\\*", "", x)
x = trimws(x)
x
}
# Feste Ankertexte laut STAI-G-Testheft, nur als Fallback wenn das labels-Attribut
# der Originalspalte fehlt oder leer ist, der Spaltenwert aber bereits eine Zahl
# 1-4 ist.
stai_anker_fest = list(
state = c("ueberhaupt nicht", "ein wenig", "ziemlich", "sehr"),
trait = c("fast nie", "manchmal", "oft", "fast immer")
)
# Extrahiert Rohwert (1-4) und Antworttext eines einzelnen STAI-Items.
# Das Exportverhalten von formr-Items vom Typ "mc" (im Unterschied zu "mc_button")
# ist in dieser Kombination nicht am realen Export verifiziert - deshalb wird hier
# NIE der numerische Rohcode direkt uebernommen. Stattdessen bestimmt die
# aufsteigend sortierte Rangfolge der Werte im labels-Attribut den Rohwert 1-4;
# der Antworttext ist der zugehoerige Attribut-Name. Ist das Attribut leer, aber
# der Spaltenwert bereits eine Zahl 1-4, wird diese Zahl direkt uebernommen (Text
# dann aus dem festen Mapping). Ist der Wert weder so noch so zuordenbar: NA.
stai_item_extrahieren = function(spalten_wert, spalten_labels, skala) {
if (length(spalten_wert) == 0 || is.na(spalten_wert[1])) {
return(list(wert = NA_real_, antwort = NA_character_))
}
wert_roh = spalten_wert[1]
if (!is.null(spalten_labels) && length(spalten_labels) > 0) {
reihenfolge = order(as.vector(spalten_labels))
lbl_sortiert = spalten_labels[reihenfolge]
pos = which(as.vector(lbl_sortiert) == suppressWarnings(as.numeric(wert_roh)))
if (length(pos) > 0 && pos[1] >= 1 && pos[1] <= 4) {
return(list(wert = as.numeric(pos[1]), antwort = names(lbl_sortiert)[pos[1]]))
}
}
wert_num = suppressWarnings(as.numeric(wert_roh))
if (!is.na(wert_num) && wert_num %in% 1:4) {
return(list(wert = wert_num, antwort = stai_anker_fest[[skala]][wert_num]))
}
list(wert = NA_real_, antwort = NA_character_)
}
# Loest den Antworttext einer labelled Spalte ueber deren labels-Attribut auf,
# NIE ueber den Rohwert direkt (z.B. bei geschlecht, dessen 1/2-Kodierung nicht
# feststeht).
stai_label_text = function(spalten_wert, spalten_labels) {
if (length(spalten_wert) == 0 || is.na(spalten_wert[1])) return(NA_character_)
if (is.null(spalten_labels) || length(spalten_labels) == 0) return(NA_character_)
pos = which(as.vector(spalten_labels) == suppressWarnings(as.numeric(spalten_wert[1])))
if (length(pos) == 0) return(NA_character_)
trimws(names(spalten_labels)[pos[1]])
}
# Polt Item-Rohwerte um (Wert = 5 - Rohwert) fuer alle Items, deren Spaltenname
# in umkehr_namen vorkommt; alle uebrigen Items bleiben unveraendert.
stai_umpolen = function(werte, umkehr_namen) {
ergebnis = ifelse(names(werte) %in% umkehr_namen, 5 - werte, werte)
names(ergebnis) = names(werte)
ergebnis
}
# Wendet die Missing-Value-Regel je Skala an: >2 fehlende Items -> nicht
# auswertbar; 1-2 fehlende Items -> Schaetzwert aus vorhandenen Items,
# aufgerundet; 0 fehlende Items -> normale Summe.
stai_score = function(werte) {
n_missing = sum(is.na(werte))
if (n_missing > 2) {
return(list(rohwert = NA_real_, missing_n = n_missing, auswertbar = FALSE))
}
if (n_missing == 0) {
rohwert = sum(werte)
} else {
rohwert = ceiling(mean(werte, na.rm = TRUE) * length(werte))
}
list(rohwert = rohwert, missing_n = n_missing, auswertbar = TRUE)
}
# Bestimmt das Normtabellen-Suffix aus dem Alter. NA (inkl. Alter < 15) bedeutet
# Fallback auf die Gesamttabelle, wird vom Aufrufer sichtbar gemacht.
stai_altersgruppe = function(alter) {
if (is.na(alter) || alter < 15) return(NA_character_)
if (alter <= 29) return("_15_29")
if (alter <= 59) return("_30_59")
return("_60plus")
}
# Exakter Rohwert-Lookup in einer Normtabelle. Kein Treffer (Rohwert ausserhalb
# der Stichprobe) -> alle Werte NA, vom Aufrufer als "kein Normwert ausgewiesen"
# anzuzeigen statt eines erratenen/interpolierten Werts.
stai_normlookup = function(tabelle, rohwert) {
zeile = tabelle[tabelle$rohwert == rohwert, , drop = FALSE]
if (nrow(zeile) == 0) {
return(list(t_wert = NA_real_, stanine = NA_real_, prozentrang = NA_real_, gefunden = FALSE))
}
list(
t_wert = suppressWarnings(as.numeric(zeile$t_wert[1])),
stanine = suppressWarnings(as.numeric(zeile$stanine[1])),
prozentrang = suppressWarnings(as.numeric(zeile$prozentrang[1])),
gefunden = TRUE
)
}
# Neutraler, farbzonenfreier Prozentrang-Balken (kein Cutoff, da im Manual
# keiner definiert ist).
stai_prozentrang_plot = function(prozentrang) {
df_marker = data.frame(x = prozentrang, y = 1)
ggplot() +
geom_segment(aes(x = 0, xend = 100, y = 1, yend = 1),
color = "#D9D9D9", linewidth = 10, lineend = "round") +
geom_point(data = df_marker, aes(x = x, y = y), shape = 24, size = 4.5,
color = "#444444", fill = "#444444") +
scale_x_continuous(limits = c(0, 100), breaks = c(0, 25, 50, 75, 100)) +
scale_y_continuous(limits = c(0.5, 1.5)) +
labs(x = "Prozentrang", y = NULL) +
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()
)
}
# Datenaufbereitung ####
stai_state_umkehr_items = c(
stai_state_01 = TRUE, stai_state_02 = TRUE, stai_state_05 = TRUE, stai_state_08 = TRUE,
stai_state_10 = TRUE, stai_state_11 = TRUE, stai_state_15 = TRUE, stai_state_16 = TRUE,
stai_state_19 = TRUE, stai_state_20 = TRUE
)
stai_trait_umkehr_items = c(
stai_trait_01 = TRUE, stai_trait_06 = TRUE, stai_trait_07 = TRUE, stai_trait_10 = TRUE,
stai_trait_13 = TRUE, stai_trait_16 = TRUE, stai_trait_19 = TRUE
)
stai_normtabellen_dateien = c(
frauen_gesamt = "frauen_gesamt.csv", frauen_15_29 = "frauen_15_29.csv",
frauen_30_59 = "frauen_30_59.csv", frauen_60plus = "frauen_60plus.csv",
maenner_gesamt = "maenner_gesamt.csv", maenner_15_29 = "maenner_15_29.csv",
maenner_30_59 = "maenner_30_59.csv", maenner_60plus = "maenner_60plus.csv"
)
stai_normtabellen = list()
for (schluessel in names(stai_normtabellen_dateien)) {
datei = stai_normtabellen_dateien[[schluessel]]
pfad = file.path(PFAD_NORMTABELLEN, datei)
if (!file.exists(pfad)) {
stop(paste0("Normtabelle nicht gefunden: ", pfad))
}
tab = tryCatch(
read.csv(pfad, stringsAsFactors = FALSE),
error = function(e) stop(paste0("Fehler beim Einlesen von '", datei, "': ", e$message))
)
for (spalte in c("rohwert", "t_wert", "stanine", "prozentrang")) {
if (!(spalte %in% names(tab))) {
stop(paste0("Normtabelle '", datei, "' hat keine Spalte '", spalte, "'."))
}
}
stai_normtabellen[[schluessel]] = tab
}
# Bekannter Vorbehalt zur Datenqualitaet (noch nicht gegen die Originalquelle
# verifiziert): frauen_60plus, rohwert = 60 -> T-Wert-Monotonie-Anomalie (67 -> 66);
# maenner_60plus, rohwert = 60 -> Prozentrang-Monotonie-Anomalie (99 -> 98).
stai_bekannte_anomalien = list(
list(tabelle = "frauen_60plus", rohwert = 60),
list(tabelle = "maenner_60plus", rohwert = 60)
)
stai_ist_bekannte_anomalie = function(tabelle, rohwert) {
any(sapply(stai_bekannte_anomalien, function(a) {
identical(a$tabelle, tabelle) && !is.na(rohwert) && identical(a$rohwert, rohwert)
}))
}
# UI ####
app_css = "
body { font-family: 'Segoe UI', Arial, sans-serif; background: #f5f5f5; }
.container-fluid { max-width: 1100px; }
.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; }
.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;
}
.skalen-reihe { display: flex; gap: 16px; flex-wrap: wrap; }
.skalen-reihe .abschnitt-karte { flex: 1; min-width: 280px; }
.meta-block { margin-bottom: 10px; color: #555; font-size: 0.95em; }
.meta-block strong { color: #222; }
.rohwert-anzeige { font-size: 1.6rem; font-weight: 700; color: #222; }
.rohwert-hinweis { color: #777; font-size: 0.88em; margin-top: 4px; }
.normtabelle-info { margin-bottom: 14px; color: #444; font-size: 0.92em; }
.disclaimer-block {
font-size: 0.82em; color: #777; font-style: italic;
margin-top: 10px; border-top: 1px solid rgba(0,0,0,.1); padding-top: 8px;
}
.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: 22px; flex-shrink: 0; }
.item-text { flex: 2; color: #333; font-size: 0.92em; }
.item-antwort { flex: 1; display: flex; justify-content: flex-end; min-width: 140px; }
.antwort-badge {
display: inline-block; border-radius: 3px; padding: 2px 10px;
font-size: 0.85em; font-weight: 700; min-width: 90px; text-align: center;
flex-shrink: 0; background-color: #EDEDED; color: #444;
}
.antwort-badge-1 { background-color: #4CAF50; color: white; }
.antwort-badge-2 { background-color: #F48FB1; color: #333333; }
.antwort-badge-3 { background-color: #EF5350; color: white; }
.antwort-badge-4 { background-color: #B71C1C; color: white; }
.antwort-badge-fehlend { background-color: #FEECEB; color: #B71C1C; }
"
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("STAI-G (State- und Trait-Angstskala)"),
tags$p("Einzelfall-Auswertung, kombinierte Erhebung")
),
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_mehrfach_ui"),
uiOutput("warnung_normtabelle_ui"),
uiOutput("normtabelle_auswahl_ui"),
uiOutput("ergebnis_ui")
)
)
# Word-Export ####
erstelle_stai_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_warnung = fp_text(font.size = 10, italic = TRUE, color = "#BF360C")
fp_disclaimer = fp_text(font.size = 9, italic = TRUE, color = "#777777")
doc = body_add_fpar(doc, fpar(ftext("STAI-G", fp_titel)))
doc = body_add_fpar(doc, fpar(
ftext("Chiffre: ", fp_label), ftext(erg$chiffre, fp_normal),
ftext(" Ausfuelldatum: ", fp_label), ftext(erg$ausfuelldatum, fp_normal),
ftext(" Alter: ", fp_label),
ftext(if (is.na(erg$alter)) "nicht auswertbar" else as.character(erg$alter), fp_normal),
ftext(" Geschlecht: ", fp_label),
ftext(if (is.na(erg$geschlecht)) "nicht zuordenbar" else erg$geschlecht, fp_normal)
))
if (!is.null(erg$mehrere_treffer_warnung)) {
doc = body_add_fpar(doc, fpar(ftext(
paste0("Mehrere Ausfuellungen gefunden (", erg$mehrere_treffer_warnung$n,
" Eintraege), es wird die neueste angezeigt."), fp_warnung
)))
}
doc = body_add_par(doc, "", style = "Normal")
doc = body_add_fpar(doc, fpar(ftext("State-Angst (aktueller Zustand)", fp_abschnitt)))
state_txt = if (isTRUE(erg$state_auswertbar)) {
sprintf("Rohwert: %d / 80", as.integer(erg$state_rohwert))
} else {
"nicht auswertbar (mehr als 2 fehlende Antworten)"
}
doc = body_add_fpar(doc, fpar(ftext(state_txt, fp_normal)))
doc = body_add_fpar(doc, fpar(ftext(
"Kein Normvergleich moeglich, Skala zur Veraenderungsmessung konstruiert.", fp_disclaimer
)))
doc = body_add_par(doc, "", style = "Normal")
doc = body_add_fpar(doc, fpar(ftext("Trait-Angst (allgemeine Aengstlichkeit)", fp_abschnitt)))
if (isTRUE(erg$trait_auswertbar)) {
doc = body_add_fpar(doc, fpar(ftext(
sprintf("Rohwert: %d / 80", as.integer(erg$trait_rohwert)), fp_normal
)))
norm_txt = if (isTRUE(erg$trait_norm_gefunden)) {
sprintf("T-Wert: %s Stanine: %s Prozentrang: %s (Normtabelle: %s)",
format(erg$trait_t_wert), format(erg$trait_stanine),
format(erg$trait_prozentrang), erg$tabelle_verwendet)
} else {
sprintf("kein Normwert ausgewiesen (Bereich der Stichprobe verlassen) (Normtabelle: %s)",
erg$tabelle_verwendet)
}
doc = body_add_fpar(doc, fpar(ftext(norm_txt, fp_normal)))
if (!is.null(erg$tabelle_anomalie_hinweis)) {
doc = body_add_fpar(doc, fpar(ftext(erg$tabelle_anomalie_hinweis, fp_warnung)))
}
if (isTRUE(erg$altersgruppen_fallback)) {
doc = body_add_fpar(doc, fpar(ftext(
"Alter ausserhalb der normierten Altersgruppen, es wurde die Gesamttabelle verwendet.",
fp_warnung
)))
}
} else {
doc = body_add_fpar(doc, fpar(ftext(
"nicht auswertbar (mehr als 2 fehlende Antworten)", fp_normal
)))
}
doc = body_add_par(doc, "", style = "Normal")
fuege_item_liste_hinzu = function(doc, titel, items) {
doc = body_add_fpar(doc, fpar(ftext(titel, fp_abschnitt)))
for (it in items) {
antwort_txt = if (is.na(it$wert) || is.na(it$antwort)) "nicht zuordenbar" else it$antwort
doc = body_add_fpar(doc, fpar(
ftext(sprintf("%2d. ", it$nr), fp_text(bold = TRUE, font.size = 10)),
ftext(paste0(it$text, " - "), fp_normal),
ftext(antwort_txt, fp_text(italic = TRUE, font.size = 10, color = "#555555"))
))
}
doc = body_add_par(doc, "", style = "Normal")
doc
}
doc = fuege_item_liste_hinzu(doc, "Trait-Items", erg$trait_items)
doc = fuege_item_liste_hinzu(doc, "State-Items", erg$state_items)
doc = body_add_fpar(doc, fpar(ftext(STAI_DISCLAIMER, fp_disclaimer)))
doc
}
# Server ####
server = function(input, output, session) {
# --- pseudonym-support-injection v1 ---
observe({
query = parseQueryString(session$clientData$url_search)
if (!is.null(query$pseudonym) && nchar(trimws(query$pseudonym)) > 0) {
updateTextInput(session, "pseudonym", value = trimws(query$pseudonym))
}
})
observe({
query = parseQueryString(session$clientData$url_search)
if (!is.null(query$chiffre) && nchar(trimws(query$chiffre)) > 0) {
updateTextInput(session, "chiffre", value = toupper(trimws(query$chiffre)))
}
})
tabelle_override = reactiveVal(NULL)
erg_aktuell = eventReactive(input$btn_suchen, {
chiffre = toupper(trimws(input$chiffre))
if ((nchar(trimws(input$pseudonym)) == 0 && nchar(chiffre) == 0) || !(nchar(trimws(input$pseudonym)) > 0 || grepl("^[A-Z][0-9]{6}$", chiffre))) {
return(list(typ = "format_fehler",
meldung = "Ungueltige Chiffre. Erwartet: ein Grossbuchstabe + 6 Ziffern (z.B. P000123)."))
}
fehlende_skripte = c(
if (!file.exists(PFAD_DOWNLOAD_SKRIPT)) PFAD_DOWNLOAD_SKRIPT,
if (!file.exists(PFAD_PSEUDONYM_SKRIPT)) PFAD_PSEUDONYM_SKRIPT
)
if (length(fehlende_skripte) > 0) {
return(list(typ = "pfad_fehler",
meldung = paste0("Skript(e) nicht gefunden:\n", paste(fehlende_skripte, collapse = "\n"))))
}
ok_dl = tryCatch(
{ source(PFAD_DOWNLOAD_SKRIPT, local = FALSE); list(ok = TRUE) },
error = function(e) list(ok = FALSE, msg = e$message)
)
if (!ok_dl$ok) {
return(list(typ = "skript_fehler",
meldung = paste0("Fehler im Download-Skript: ", ok_dl$msg)))
}
if (!exists("daten_stai_state_trait", envir = .GlobalEnv) ||
!is.data.frame(get("daten_stai_state_trait", envir = .GlobalEnv))) {
return(list(typ = "daten_fehler",
meldung = "Objekt 'daten_stai_state_trait' nach dem Sourcen nicht gefunden oder kein Dataframe."))
}
daten_stai_state_trait = get("daten_stai_state_trait", envir = .GlobalEnv)
db_ordner = local({
ordner = dirname(normalizePath(PFAD_PSEUDONYM_SKRIPT, mustWork = FALSE))
gefunden = NULL
for (i in 1:5) {
if (file.exists(file.path(ordner, "pseudonyme.db"))) {
gefunden = ordner
break
}
elternteil = dirname(ordner)
if (elternteil == ordner) break
ordner = elternteil
}
gefunden
})
if (is.null(db_ordner)) {
return(list(typ = "db_fehler",
meldung = paste0("pseudonyme.db nicht gefunden (bis 5 Ebenen oberhalb von ",
dirname(normalizePath(PFAD_PSEUDONYM_SKRIPT, mustWork = FALSE)), " gesucht).")))
}
alter_wd = getwd()
setwd(db_ordner)
on.exit(setwd(alter_wd), add = TRUE)
ok_ps = tryCatch(
{ source(PFAD_PSEUDONYM_SKRIPT, local = FALSE)
if (nchar(trimws(input$pseudonym)) > 0) {
.pw_wert = trimws(input$pseudonym)
.pw_tab = get("pseudo", envir = .GlobalEnv)
.pw_treffer = .pw_tab[.pw_tab$pseudonym == .pw_wert, ]
if (nrow(.pw_treffer) > 0) chiffre = toupper(trimws(.pw_treffer$chiffre[1]))
}; list(ok = TRUE) },
error = function(e) list(ok = FALSE, msg = e$message)
)
if (!ok_ps$ok) {
return(list(typ = "db_fehler",
meldung = paste0("Fehler im Pseudonym-Skript: ", ok_ps$msg)))
}
if (!exists("pseudo", envir = .GlobalEnv) || !is.data.frame(get("pseudo", envir = .GlobalEnv))) {
return(list(typ = "db_fehler",
meldung = "Objekt 'pseudo' nach dem Sourcen nicht gefunden oder kein Dataframe."))
}
pseudo = get("pseudo", envir = .GlobalEnv)
treffer_ps = pseudo[toupper(trimws(as.character(pseudo$chiffre))) == chiffre, ]
if (nrow(treffer_ps) == 0) {
return(list(typ = "chiffre_nicht_gefunden",
meldung = paste0("Chiffre '", chiffre, "' wurde in der Pseudonym-Datenbank nicht gefunden.")))
}
session_ids = unique(as.character(treffer_ps$pseudonym))
if (nchar(trimws(input$pseudonym)) > 0) session_ids = trimws(input$pseudonym)
treffer_dat = daten_stai_state_trait[as.character(daten_stai_state_trait$session) %in% session_ids, , drop = FALSE]
if (nrow(treffer_dat) == 0) {
return(list(typ = "session_nicht_gefunden",
meldung = paste0("Kein STAI-Datensatz fuer Chiffre '", chiffre, "' gefunden. (",
length(session_ids), " Pseudonym(e) geprueft)")))
}
mehrere_treffer_warnung = NULL
if (nrow(treffer_dat) > 1) {
n = nrow(treffer_dat)
idx_neu = which.max(as.POSIXct(treffer_dat$created))
treffer_dat = treffer_dat[idx_neu, , drop = FALSE]
mehrere_treffer_warnung = list(n = n)
}
zeile = treffer_dat[1, , drop = FALSE]
ausfuelldatum = tryCatch(
format(as.POSIXct(zeile[["created"]][1]), "%d.%m.%Y"),
error = function(e) "unbekannt"
)
alter_roh = suppressWarnings(as.numeric(zeile[["alter"]][1]))
alter = if (!is.na(alter_roh) && alter_roh > 0) alter_roh else NA_real_
geschlecht_txt = stai_label_text(zeile[["geschlecht"]], attr(daten_stai_state_trait[["geschlecht"]], "labels"))
geschlecht = if (!is.na(geschlecht_txt) && grepl("^weiblich$", geschlecht_txt, ignore.case = TRUE)) {
"weiblich"
} else if (!is.na(geschlecht_txt) && grepl("^m(a|ä)nnlich$", geschlecht_txt, ignore.case = TRUE)) {
"maennlich"
} else {
NA_character_
}
extrahiere_items = function(praefix, skala) {
cols = sprintf("%s_%02d", praefix, 1:20)
lapply(cols, function(col) {
if (!(col %in% names(daten_stai_state_trait))) {
stop(paste0("Item-Spalte '", col, "' fehlt in daten_stai_state_trait."))
}
ex = stai_item_extrahieren(zeile[[col]], attr(daten_stai_state_trait[[col]], "labels"), skala)
label_roh = attr(daten_stai_state_trait[[col]], "label")
list(
col = col,
nr = as.integer(sub(paste0("^", praefix, "_"), "", col)),
text = bereinige_markdown(if (is.null(label_roh)) "" else label_roh),
wert = ex$wert,
antwort = ex$antwort
)
})
}
state_items = extrahiere_items("stai_state", "state")
trait_items = extrahiere_items("stai_trait", "trait")
state_werte = setNames(sapply(state_items, function(x) x$wert), sapply(state_items, function(x) x$col))
trait_werte = setNames(sapply(trait_items, function(x) x$wert), sapply(trait_items, function(x) x$col))
state_umgepolt = stai_umpolen(state_werte, names(stai_state_umkehr_items))
trait_umgepolt = stai_umpolen(trait_werte, names(stai_trait_umkehr_items))
state_items = lapply(state_items, function(it) c(it, list(umgepolt = unname(state_umgepolt[[it$col]]))))
trait_items = lapply(trait_items, function(it) c(it, list(umgepolt = unname(trait_umgepolt[[it$col]]))))
state_score = stai_score(state_umgepolt)
trait_score = stai_score(trait_umgepolt)
geschlecht_praefix = if (!is.na(geschlecht) && geschlecht == "weiblich") {
"frauen"
} else if (!is.na(geschlecht) && geschlecht == "maennlich") {
"maenner"
} else {
NA_character_
}
altersgruppen_suffix = stai_altersgruppe(alter)
altersgruppen_fallback = is.na(altersgruppen_suffix)
if (altersgruppen_fallback) altersgruppen_suffix = "_gesamt"
tabelle_vorschlag = if (!is.na(geschlecht_praefix)) {
paste0(geschlecht_praefix, altersgruppen_suffix)
} else {
paste0("frauen", altersgruppen_suffix)
}
geschlecht_unbekannt = is.na(geschlecht_praefix)
list(
typ = "ok",
chiffre = chiffre,
ausfuelldatum = ausfuelldatum,
alter = alter,
geschlecht = geschlecht,
mehrere_treffer_warnung = mehrere_treffer_warnung,
state_rohwert = state_score$rohwert,
state_missing_n = state_score$missing_n,
state_auswertbar = state_score$auswertbar,
trait_rohwert = trait_score$rohwert,
trait_missing_n = trait_score$missing_n,
trait_auswertbar = trait_score$auswertbar,
tabelle_vorschlag = tabelle_vorschlag,
altersgruppen_fallback = altersgruppen_fallback,
geschlecht_unbekannt = geschlecht_unbekannt,
state_items = state_items,
trait_items = trait_items
)
})
observeEvent(erg_aktuell(), {
d = erg_aktuell()
if (identical(d$typ, "ok")) tabelle_override(d$tabelle_vorschlag)
})
observeEvent(input$normtabelle_wahl, {
req(input$normtabelle_wahl)
tabelle_override(input$normtabelle_wahl)
}, ignoreInit = TRUE)
trait_norm_aktuell = reactive({
d = erg_aktuell()
req(identical(d$typ, "ok"))
if (!isTRUE(d$trait_auswertbar)) return(list(auswertbar = FALSE))
tab_name = tabelle_override()
req(!is.null(tab_name))
tab = stai_normtabellen[[tab_name]]
req(!is.null(tab))
lk = stai_normlookup(tab, d$trait_rohwert)
anomalie_hinweis = if (stai_ist_bekannte_anomalie(tab_name, d$trait_rohwert)) {
"Dieser Normwert basiert auf einer noch nicht gegengeprueften Tabellenzelle, siehe Dokumentation."
} else {
NULL
}
list(
auswertbar = TRUE,
gefunden = lk$gefunden,
t_wert = lk$t_wert,
stanine = lk$stanine,
prozentrang = lk$prozentrang,
tabelle_verwendet = tab_name,
anomalie_hinweis = anomalie_hinweis
)
})
output$fehler_ui = renderUI({
req(input$btn_suchen)
d = erg_aktuell()
if (!identical(d$typ, "ok")) div(class = "alert-fehler", d$meldung)
})
output$warnung_mehrfach_ui = renderUI({
req(input$btn_suchen)
d = erg_aktuell()
if (!identical(d$typ, "ok") || is.null(d$mehrere_treffer_warnung)) return(NULL)
div(class = "alert-warnung",
paste0("Mehrere Ausfuellungen gefunden (", d$mehrere_treffer_warnung$n,
" Eintraege). Angezeigt wird die neueste.")
)
})
output$warnung_normtabelle_ui = renderUI({
req(input$btn_suchen)
d = erg_aktuell()
if (!identical(d$typ, "ok") || !isTRUE(d$geschlecht_unbekannt)) return(NULL)
div(class = "alert-warnung",
"Geschlecht nicht eindeutig zuordenbar. Bitte Normtabelle manuell pruefen und auswaehlen."
)
})
output$normtabelle_auswahl_ui = renderUI({
req(input$btn_suchen)
d = erg_aktuell()
if (!identical(d$typ, "ok")) return(NULL)
div(class = "abschnitt-karte",
div(class = "abschnitt-titel", "Normtabelle (Trait-Skala)"),
selectInput("normtabelle_wahl", label = "Verwendete Normtabelle",
choices = names(stai_normtabellen_dateien),
selected = d$tabelle_vorschlag, width = "100%")
)
})
output$ergebnis_ui = renderUI({
req(input$btn_suchen)
d = erg_aktuell()
if (!identical(d$typ, "ok")) return(NULL)
tn = trait_norm_aktuell()
kopfzeile = div(class = "meta-block",
tags$strong("Chiffre: "), d$chiffre,
tags$span(" | ", style = "color:#ccc;"),
tags$strong("Ausfuelldatum: "), d$ausfuelldatum,
tags$span(" | ", style = "color:#ccc;"),
tags$strong("Alter: "), if (is.na(d$alter)) "nicht auswertbar" else d$alter,
tags$span(" | ", style = "color:#ccc;"),
tags$strong("Geschlecht: "), if (is.na(d$geschlecht)) "nicht zuordenbar" else d$geschlecht
)
state_karte = div(class = "abschnitt-karte",
div(class = "abschnitt-titel", "State-Angst (aktueller Zustand)"),
if (isTRUE(d$state_auswertbar)) {
tagList(
div(class = "rohwert-anzeige", paste0(as.integer(d$state_rohwert), " / 80")),
div(class = "rohwert-hinweis",
"kein Normvergleich moeglich, Skala zur Veraenderungsmessung konstruiert")
)
} else {
div(class = "rohwert-anzeige", "nicht auswertbar (mehr als 2 fehlende Antworten)")
}
)
trait_karte = div(class = "abschnitt-karte",
div(class = "abschnitt-titel", "Trait-Angst (allgemeine Aengstlichkeit)"),
if (!isTRUE(d$trait_auswertbar)) {
div(class = "rohwert-anzeige", "nicht auswertbar (mehr als 2 fehlende Antworten)")
} else {
tagList(
div(class = "rohwert-anzeige", paste0(as.integer(d$trait_rohwert), " / 80")),
if (isTRUE(tn$gefunden)) {
div(class = "normtabelle-info",
tags$strong("T-Wert: "), format(tn$t_wert),
tags$span(" | ", style = "color:#ccc;"),
tags$strong("Stanine: "), format(tn$stanine),
tags$span(" | ", style = "color:#ccc;"),
tags$strong("Prozentrang: "), format(tn$prozentrang),
tags$span(" | ", style = "color:#ccc;"),
tags$em(paste0("Normtabelle: ", tn$tabelle_verwendet))
)
} else {
div(class = "normtabelle-info", "kein Normwert ausgewiesen (Bereich der Stichprobe verlassen)")
},
if (!is.null(tn$anomalie_hinweis)) div(class = "alert-warnung", tn$anomalie_hinweis),
if (isTRUE(d$altersgruppen_fallback)) {
div(class = "alert-warnung",
paste0("Alter ausserhalb der normierten Altersgruppen, es wird die Gesamttabelle fuer ",
if (grepl("^frauen", tn$tabelle_verwendet)) "Frauen" else "Maenner", " verwendet.")
)
},
if (isTRUE(tn$gefunden)) plotOutput("prozentrang_plot", height = "110px")
)
}
)
item_liste = function(items, titel) {
unresolved_n = sum(sapply(items, function(x) is.na(x$wert)))
div(class = "abschnitt-karte",
div(class = "abschnitt-titel", titel),
if (unresolved_n > 0) {
div(class = "alert-warnung", paste0(unresolved_n, " von 20 ", titel, " nicht zuordenbar."))
},
lapply(items, function(it) {
unresolved = is.na(it$wert) || is.na(it$antwort)
antwort_anzeige = if (unresolved) "nicht zuordenbar" else it$antwort
stufe_key = if (!unresolved && !is.na(it$umgepolt) && as.character(it$umgepolt) %in% names(STAI_BADGE_FARBEN)) {
as.character(it$umgepolt)
} else {
NA_character_
}
badge_klasse = if (unresolved || is.na(stufe_key)) {
"antwort-badge antwort-badge-fehlend"
} else {
paste0("antwort-badge antwort-badge-", stufe_key)
}
div(class = "item-zeile",
span(class = "item-nr", it$nr),
span(class = "item-text", it$text),
span(class = "item-antwort",
tags$span(class = badge_klasse, antwort_anzeige)
)
)
})
)
}
tagList(
kopfzeile,
div(class = "skalen-reihe", state_karte, trait_karte),
item_liste(d$trait_items, "Trait-Items"),
item_liste(d$state_items, "State-Items"),
div(class = "disclaimer-block", STAI_DISCLAIMER)
)
})
output$prozentrang_plot = renderPlot({
tn = trait_norm_aktuell()
req(isTRUE(tn$gefunden))
stai_prozentrang_plot(tn$prozentrang)
})
output$download_word = downloadHandler(
filename = function() {
d = tryCatch(erg_aktuell(), error = function(e) NULL)
chiffre_esc = if (is.list(d) && identical(d$typ, "ok") && nchar(d$chiffre) > 0) {
gsub("[^A-Za-z0-9_-]", "_", d$chiffre)
} else {
"export"
}
ausfuelldatum_fn = tryCatch(
format(as.Date(d$ausfuelldatum, "%d.%m.%Y"), "%Y%m%d"),
error = function(e) format(Sys.Date(), "%Y%m%d")
)
if (is.na(ausfuelldatum_fn) || length(ausfuelldatum_fn) == 0) {
ausfuelldatum_fn = format(Sys.Date(), "%Y%m%d")
}
paste0("STAI_", chiffre_esc, "_", ausfuelldatum_fn, ".docx")
},
content = function(file) {
d = tryCatch(erg_aktuell(), error = function(e) NULL)
daten_ok = is.list(d) && identical(d$typ, "ok")
if (!daten_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()
}
tn = trait_norm_aktuell()
erg = c(d, list(
trait_norm_gefunden = isTRUE(tn$gefunden),
trait_t_wert = tn$t_wert,
trait_stanine = tn$stanine,
trait_prozentrang = tn$prozentrang,
tabelle_verwendet = tn$tabelle_verwendet,
tabelle_anomalie_hinweis = tn$anomalie_hinweis
))
doc = tryCatch(
erstelle_stai_docx(erg),
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)

View file

@ -0,0 +1,59 @@
rohwert,t_wert,stanine,prozentrang
20,26,1,1
21,30,1,2
22,32,1,4
23,35,2,6
24,37,2,10
25,39,3,13
26,40,3,16
27,42,3,20
28,44,4,26
29,45,4,30
30,46,4,34
31,47,4,39
32,48,5,44
33,49,5,47
34,50,5,51
35,51,5,53
36,51,5,56
37,52,5,59
38,53,6,63
39,54,6,66
40,55,6,68
41,56,6,71
42,56,6,74
43,57,6,77
44,58,7,79
45,59,7,81
46,60,7,83
47,61,7,86
48,62,7,88
49,63,8,90
50,64,8,91
51,65,8,93
52,66,8,94
53,67,8,95
54,68,9,96
55,69,9,97
56,70,9,98
57,70,9,98
58,71,9,98
59,71,9,98
60,71,9,98
61,72,9,99
62,73,9,99
63,73,9,99
64,74,9,99
65,74,9,99
66,74,9,99
67,75,9,99
68,76,9,100
69,78,9,100
70,78,9,100
71,78,9,100
72,78,9,100
73,78,9,100
74,78,9,100
75,78,9,100
76,78,9,100
77,80,9,100
1 rohwert t_wert stanine prozentrang
2 20 26 1 1
3 21 30 1 2
4 22 32 1 4
5 23 35 2 6
6 24 37 2 10
7 25 39 3 13
8 26 40 3 16
9 27 42 3 20
10 28 44 4 26
11 29 45 4 30
12 30 46 4 34
13 31 47 4 39
14 32 48 5 44
15 33 49 5 47
16 34 50 5 51
17 35 51 5 53
18 36 51 5 56
19 37 52 5 59
20 38 53 6 63
21 39 54 6 66
22 40 55 6 68
23 41 56 6 71
24 42 56 6 74
25 43 57 6 77
26 44 58 7 79
27 45 59 7 81
28 46 60 7 83
29 47 61 7 86
30 48 62 7 88
31 49 63 8 90
32 50 64 8 91
33 51 65 8 93
34 52 66 8 94
35 53 67 8 95
36 54 68 9 96
37 55 69 9 97
38 56 70 9 98
39 57 70 9 98
40 58 71 9 98
41 59 71 9 98
42 60 71 9 98
43 61 72 9 99
44 62 73 9 99
45 63 73 9 99
46 64 74 9 99
47 65 74 9 99
48 66 74 9 99
49 67 75 9 99
50 68 76 9 100
51 69 78 9 100
52 70 78 9 100
53 71 78 9 100
54 72 78 9 100
55 73 78 9 100
56 74 78 9 100
57 75 78 9 100
58 76 78 9 100
59 77 80 9 100

View file

@ -0,0 +1,53 @@
rohwert,t_wert,stanine,prozentrang
20,23,1,1
21,26,1,1
22,29,1,2
23,32,1,4
24,35,2,6
25,37,2,9
26,38,3,12
27,40,3,16
28,41,3,20
29,43,3,23
30,44,4,27
31,45,4,32
32,46,4,36
33,47,4,39
34,48,5,43
35,49,5,47
36,50,5,51
37,51,5,56
38,52,5,59
39,53,6,63
40,54,6,66
41,55,6,69
42,56,6,72
43,57,6,75
44,58,7,78
45,59,7,81
46,59,7,82
47,60,7,84
48,61,7,86
49,62,7,88
50,62,7,89
51,63,8,91
52,64,8,92
53,65,8,93
54,66,8,95
55,67,8,96
56,68,9,97
57,70,9,97
58,70,9,98
59,71,9,98
60,72,9,99
61,73,9,99
62,74,9,99
63,76,9,99
64,76,9,99
65,77,9,100
66,79,9,100
67,80,9,100
68,80,9,100
69,80,9,100
70,80,9,100
71,82,9,100
1 rohwert t_wert stanine prozentrang
2 20 23 1 1
3 21 26 1 1
4 22 29 1 2
5 23 32 1 4
6 24 35 2 6
7 25 37 2 9
8 26 38 3 12
9 27 40 3 16
10 28 41 3 20
11 29 43 3 23
12 30 44 4 27
13 31 45 4 32
14 32 46 4 36
15 33 47 4 39
16 34 48 5 43
17 35 49 5 47
18 36 50 5 51
19 37 51 5 56
20 38 52 5 59
21 39 53 6 63
22 40 54 6 66
23 41 55 6 69
24 42 56 6 72
25 43 57 6 75
26 44 58 7 78
27 45 59 7 81
28 46 59 7 82
29 47 60 7 84
30 48 61 7 86
31 49 62 7 88
32 50 62 7 89
33 51 63 8 91
34 52 64 8 92
35 53 65 8 93
36 54 66 8 95
37 55 67 8 96
38 56 68 9 97
39 57 70 9 97
40 58 70 9 98
41 59 71 9 98
42 60 72 9 99
43 61 73 9 99
44 62 74 9 99
45 63 76 9 99
46 64 76 9 99
47 65 77 9 100
48 66 79 9 100
49 67 80 9 100
50 68 80 9 100
51 69 80 9 100
52 70 80 9 100
53 71 82 9 100

View file

@ -0,0 +1,51 @@
rohwert,t_wert,stanine,prozentrang
20,24,1,1
21,29,1,2
22,30,1,2
23,32,1,3
24,34,2,5
25,35,2,7
26,37,2,10
27,38,3,11
28,39,3,14
29,40,3,17
30,42,3,20
31,43,4,23
32,44,4,27
33,45,4,31
34,46,4,35
35,47,4,37
36,47,4,40
37,48,5,43
38,49,5,46
39,49,5,50
40,51,5,55
41,52,5,58
42,53,6,61
43,54,6,63
44,54,6,66
45,55,6,68
46,55,6,70
47,56,6,71
48,56,6,74
49,57,7,77
50,58,7,80
51,59,7,82
52,60,7,84
53,60,7,85
54,61,7,87
55,63,8,90
56,64,8,92
57,66,8,93
58,66,8,94
59,67,8,95
60,66,8,95
61,67,8,96
62,68,9,96
63,68,9,97
64,69,9,98
65,70,9,98
66,70,9,98
67,71,9,99
68,73,9,99
69,78,9,100
1 rohwert t_wert stanine prozentrang
2 20 24 1 1
3 21 29 1 2
4 22 30 1 2
5 23 32 1 3
6 24 34 2 5
7 25 35 2 7
8 26 37 2 10
9 27 38 3 11
10 28 39 3 14
11 29 40 3 17
12 30 42 3 20
13 31 43 4 23
14 32 44 4 27
15 33 45 4 31
16 34 46 4 35
17 35 47 4 37
18 36 47 4 40
19 37 48 5 43
20 38 49 5 46
21 39 49 5 50
22 40 51 5 55
23 41 52 5 58
24 42 53 6 61
25 43 54 6 63
26 44 54 6 66
27 45 55 6 68
28 46 55 6 70
29 47 56 6 71
30 48 56 6 74
31 49 57 7 77
32 50 58 7 80
33 51 59 7 82
34 52 60 7 84
35 53 60 7 85
36 54 61 7 87
37 55 63 8 90
38 56 64 8 92
39 57 66 8 93
40 58 66 8 94
41 59 67 8 95
42 60 66 8 95
43 61 67 8 96
44 62 68 9 96
45 63 68 9 97
46 64 69 9 98
47 65 70 9 98
48 66 70 9 98
49 67 71 9 99
50 68 73 9 99
51 69 78 9 100

View file

@ -0,0 +1,59 @@
rohwert,t_wert,stanine,prozentrang
20,24,1,1
21,28,1,1
22,31,1,3
23,33,2,4
24,35,2,7
25,37,2,10
26,39,3,13
27,40,3,17
28,42,3,20
29,43,4,24
30,44,4,28
31,45,4,33
32,47,4,37
33,47,4,40
34,48,5,44
35,49,5,47
36,50,5,51
37,51,5,55
38,52,5,58
39,53,6,62
40,54,6,65
41,55,6,68
42,55,6,71
43,56,6,74
44,57,6,76
45,58,7,79
46,59,7,81
47,59,7,83
48,60,7,85
49,61,7,87
50,62,7,88
51,63,8,90
52,64,8,91
53,64,8,92
54,66,8,94
55,67,8,95
56,68,9,96
57,69,9,97
58,69,9,97
59,70,9,98
60,70,9,98
61,71,9,98
62,72,9,99
63,73,9,99
64,73,9,99
65,74,9,99
66,75,9,99
67,76,9,99
68,77,9,100
69,79,9,100
70,80,9,100
71,80,9,100
72,81,9,100
73,81,9,100
74,81,9,100
75,81,9,100
76,81,9,100
77,83,9,100
1 rohwert t_wert stanine prozentrang
2 20 24 1 1
3 21 28 1 1
4 22 31 1 3
5 23 33 2 4
6 24 35 2 7
7 25 37 2 10
8 26 39 3 13
9 27 40 3 17
10 28 42 3 20
11 29 43 4 24
12 30 44 4 28
13 31 45 4 33
14 32 47 4 37
15 33 47 4 40
16 34 48 5 44
17 35 49 5 47
18 36 50 5 51
19 37 51 5 55
20 38 52 5 58
21 39 53 6 62
22 40 54 6 65
23 41 55 6 68
24 42 55 6 71
25 43 56 6 74
26 44 57 6 76
27 45 58 7 79
28 46 59 7 81
29 47 59 7 83
30 48 60 7 85
31 49 61 7 87
32 50 62 7 88
33 51 63 8 90
34 52 64 8 91
35 53 64 8 92
36 54 66 8 94
37 55 67 8 95
38 56 68 9 96
39 57 69 9 97
40 58 69 9 97
41 59 70 9 98
42 60 70 9 98
43 61 71 9 98
44 62 72 9 99
45 63 73 9 99
46 64 73 9 99
47 65 74 9 99
48 66 75 9 99
49 67 76 9 99
50 68 77 9 100
51 69 79 9 100
52 70 80 9 100
53 71 80 9 100
54 72 81 9 100
55 73 81 9 100
56 74 81 9 100
57 75 81 9 100
58 76 81 9 100
59 77 83 9 100

View file

@ -0,0 +1,44 @@
rohwert,t_wert,stanine,prozentrang
20,27,1,1
21,31,1,3
22,32,2,3
23,33,2,5
24,34,2,6
25,36,2,8
26,38,3,13
27,41,3,17
28,42,3,22
29,44,4,27
30,46,4,33
31,47,4,40
32,49,5,45
33,50,5,49
34,51,5,54
35,52,5,59
36,54,6,64
37,55,6,67
38,56,6,71
39,57,6,74
40,57,6,76
41,58,7,80
42,59,7,82
43,60,7,85
44,62,7,88
45,62,8,89
46,63,8,91
47,64,8,92
48,65,8,93
49,65,8,94
50,66,8,94
51,67,8,96
52,67,8,97
53,68,9,97
54,70,9,98
55,70,9,98
56,70,9,98
57,71,9,98
58,72,9,99
59,73,9,99
60,75,9,99
61,77,9,100
62,79,9,100
1 rohwert t_wert stanine prozentrang
2 20 27 1 1
3 21 31 1 3
4 22 32 2 3
5 23 33 2 5
6 24 34 2 6
7 25 36 2 8
8 26 38 3 13
9 27 41 3 17
10 28 42 3 22
11 29 44 4 27
12 30 46 4 33
13 31 47 4 40
14 32 49 5 45
15 33 50 5 49
16 34 51 5 54
17 35 52 5 59
18 36 54 6 64
19 37 55 6 67
20 38 56 6 71
21 39 57 6 74
22 40 57 6 76
23 41 58 7 80
24 42 59 7 82
25 43 60 7 85
26 44 62 7 88
27 45 62 8 89
28 46 63 8 91
29 47 64 8 92
30 48 65 8 93
31 49 65 8 94
32 50 66 8 94
33 51 67 8 96
34 52 67 8 97
35 53 68 9 97
36 54 70 9 98
37 55 70 9 98
38 56 70 9 98
39 57 71 9 98
40 58 72 9 99
41 59 73 9 99
42 60 75 9 99
43 61 77 9 100
44 62 79 9 100

View file

@ -0,0 +1,48 @@
rohwert,t_wert,stanine,prozentrang
20,25,1,1
21,29,1,2
22,32,1,3
23,34,2,6
24,37,2,9
25,38,3,12
26,40,3,16
27,42,3,20
28,43,4,25
29,45,4,30
30,46,4,36
31,48,5,41
32,49,5,45
33,50,5,50
34,51,5,54
35,52,5,59
36,53,6,63
37,55,6,67
38,56,6,70
39,57,6,73
40,57,6,76
41,58,7,78
42,58,7,80
43,59,7,82
44,60,7,84
45,61,7,86
46,61,7,87
47,62,7,88
48,63,8,90
49,63,8,92
50,64,8,92
51,65,8,93
52,66,8,94
53,67,8,96
54,68,9,97
55,68,9,97
56,70,9,98
57,71,9,98
58,71,9,98
59,72,9,99
60,74,9,99
61,77,9,100
62,77,9,100
63,78,9,100
64,78,9,100
65,78,9,100
66,80,9,100
1 rohwert t_wert stanine prozentrang
2 20 25 1 1
3 21 29 1 2
4 22 32 1 3
5 23 34 2 6
6 24 37 2 9
7 25 38 3 12
8 26 40 3 16
9 27 42 3 20
10 28 43 4 25
11 29 45 4 30
12 30 46 4 36
13 31 48 5 41
14 32 49 5 45
15 33 50 5 50
16 34 51 5 54
17 35 52 5 59
18 36 53 6 63
19 37 55 6 67
20 38 56 6 70
21 39 57 6 73
22 40 57 6 76
23 41 58 7 78
24 42 58 7 80
25 43 59 7 82
26 44 60 7 84
27 45 61 7 86
28 46 61 7 87
29 47 62 7 88
30 48 63 8 90
31 49 63 8 92
32 50 64 8 92
33 51 65 8 93
34 52 66 8 94
35 53 67 8 96
36 54 68 9 97
37 55 68 9 97
38 56 70 9 98
39 57 71 9 98
40 58 71 9 98
41 59 72 9 99
42 60 74 9 99
43 61 77 9 100
44 62 77 9 100
45 63 78 9 100
46 64 78 9 100
47 65 78 9 100
48 66 80 9 100

View file

@ -0,0 +1,51 @@
rohwert,t_wert,stanine,prozentrang
20,26,1,1
21,29,1,2
22,32,1,3
23,36,2,8
24,39,3,14
25,41,3,19
26,42,3,22
27,44,4,27
28,46,4,34
29,47,4,39
30,48,5,42
31,49,5,45
32,49,5,50
33,52,5,56
34,52,5,60
35,53,6,63
36,54,6,67
37,55,6,69
38,56,6,73
39,57,6,76
40,58,7,79
41,59,7,83
42,61,7,86
43,62,7,89
44,63,8,90
45,63,8,91
46,65,8,92
47,65,8,93
48,65,8,93
49,65,8,94
50,66,8,95
51,66,8,95
52,66,8,96
53,68,9,97
54,68,9,97
55,68,9,98
56,68,9,98
57,69,9,98
58,70,9,98
59,70,9,99
60,71,9,98
61,71,9,98
62,71,9,98
63,71,9,99
64,71,9,99
65,73,9,99
66,74,9,99
67,74,9,99
68,74,9,99
69,77,9,100
1 rohwert t_wert stanine prozentrang
2 20 26 1 1
3 21 29 1 2
4 22 32 1 3
5 23 36 2 8
6 24 39 3 14
7 25 41 3 19
8 26 42 3 22
9 27 44 4 27
10 28 46 4 34
11 29 47 4 39
12 30 48 5 42
13 31 49 5 45
14 32 49 5 50
15 33 52 5 56
16 34 52 5 60
17 35 53 6 63
18 36 54 6 67
19 37 55 6 69
20 38 56 6 73
21 39 57 6 76
22 40 58 7 79
23 41 59 7 83
24 42 61 7 86
25 43 62 7 89
26 44 63 8 90
27 45 63 8 91
28 46 65 8 92
29 47 65 8 93
30 48 65 8 93
31 49 65 8 94
32 50 66 8 95
33 51 66 8 95
34 52 66 8 96
35 53 68 9 97
36 54 68 9 97
37 55 68 9 98
38 56 68 9 98
39 57 69 9 98
40 58 70 9 98
41 59 70 9 99
42 60 71 9 98
43 61 71 9 98
44 62 71 9 98
45 63 71 9 99
46 64 71 9 99
47 65 73 9 99
48 66 74 9 99
49 67 74 9 99
50 68 74 9 99
51 69 77 9 100

View file

@ -0,0 +1,51 @@
rohwert,t_wert,stanine,prozentrang
20,26,1,1
21,29,1,2
22,32,1,3
23,34,2,6
24,36,2,9
25,38,3,12
26,40,3,16
27,42,3,20
28,43,4,25
29,45,4,31
30,46,4,36
31,48,5,41
32,49,5,46
33,50,5,50
34,51,5,55
35,52,5,60
36,54,6,64
37,55,6,67
38,56,6,71
39,57,6,74
40,57,6,76
41,58,7,79
42,59,7,81
43,60,7,84
44,61,7,86
45,61,7,87
46,62,7,88
47,63,8,90
48,63,8,91
49,64,8,93
50,65,8,93
51,66,8,94
52,67,8,95
53,68,9,96
54,69,9,97
55,70,9,98
56,70,9,98
57,71,9,98
58,71,9,98
59,72,9,99
60,74,9,99
61,76,9,99
62,76,9,100
63,77,9,100
64,77,9,100
65,77,9,100
66,79,9,100
67,81,9,100
68,81,9,100
69,83,9,100
1 rohwert t_wert stanine prozentrang
2 20 26 1 1
3 21 29 1 2
4 22 32 1 3
5 23 34 2 6
6 24 36 2 9
7 25 38 3 12
8 26 40 3 16
9 27 42 3 20
10 28 43 4 25
11 29 45 4 31
12 30 46 4 36
13 31 48 5 41
14 32 49 5 46
15 33 50 5 50
16 34 51 5 55
17 35 52 5 60
18 36 54 6 64
19 37 55 6 67
20 38 56 6 71
21 39 57 6 74
22 40 57 6 76
23 41 58 7 79
24 42 59 7 81
25 43 60 7 84
26 44 61 7 86
27 45 61 7 87
28 46 62 7 88
29 47 63 8 90
30 48 63 8 91
31 49 64 8 93
32 50 65 8 93
33 51 66 8 94
34 52 67 8 95
35 53 68 9 96
36 54 69 9 97
37 55 70 9 98
38 56 70 9 98
39 57 71 9 98
40 58 71 9 98
41 59 72 9 99
42 60 74 9 99
43 61 76 9 99
44 62 76 9 100
45 63 77 9 100
46 64 77 9 100
47 65 77 9 100
48 66 79 9 100
49 67 81 9 100
50 68 81 9 100
51 69 83 9 100

3019
STAI/renv.lock Normal file

File diff suppressed because it is too large Load diff