Initial commit
This commit is contained in:
commit
3cba772836
1341 changed files with 532924 additions and 0 deletions
215
#alt_skripte/get_data.R
Normal file
215
#alt_skripte/get_data.R
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
library(formr)
|
||||
formr_store_keys(host = "https://diagnostik.karneboge.de/api", client_id = "a007099d1ce340b2c701910b01d821ae", client_secret = "ff3f43bdca93c92fa5a854c97fbefc0d67f1900545fc79745a6e6565c1cbc07b", verbose = F)
|
||||
formr_api_authenticate(host = "https://diagnostik.karneboge.de/api", verbose = F)
|
||||
|
||||
#formr_api_surveys(name_pattern = NULL, verbose = TRUE)
|
||||
|
||||
daten_scid5spq=formr_api_results(
|
||||
run_name = "SKID5-SPQ",
|
||||
"scid5spq",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_bdi2=formr_api_results(
|
||||
run_name = "BDI2",
|
||||
"bdi2",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_itq=formr_api_results(
|
||||
run_name = "ITQ",
|
||||
"itq",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
daten_vds90=formr_api_results(
|
||||
run_name = "VDS90",
|
||||
"vds90",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_scl90r=formr_api_results(
|
||||
run_name = "SCL90R",
|
||||
"scl90r",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_pcl5lec5=formr_api_results(
|
||||
run_name = "PCL5-LEC5",
|
||||
"plc5_lec5",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_pcl5=formr_api_results(
|
||||
run_name = "PCL5",
|
||||
"plc5",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_pg13r=formr_api_results(
|
||||
run_name = "PG13r",
|
||||
"pg13r",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_ctq=formr_api_results(
|
||||
run_name = "CTQ",
|
||||
"ctq",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_bitc=formr_api_results(
|
||||
run_name = "BIT-C",
|
||||
"bit_c",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_acqbsqmi=formr_api_results(
|
||||
run_name = "ACQ-BSQ-MI",
|
||||
"acq_bsq_mi",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_lsas=formr_api_results(
|
||||
run_name = "LSAS",
|
||||
"lsas",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_desci=formr_api_results(
|
||||
run_name = "DESC-I",
|
||||
"desc_i",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_descii=formr_api_results(
|
||||
run_name = "DESC-II",
|
||||
"desc_ii",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_cuditr=formr_api_results(
|
||||
run_name = "CUDIT",
|
||||
"cudit",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_audit=formr_api_results(
|
||||
run_name = "AUDIT",
|
||||
"audit",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_pssi=formr_api_results(
|
||||
run_name = "PSSI",
|
||||
"pssi",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_iss20r=formr_api_results(
|
||||
run_name = "ISS20R",
|
||||
"iss20r",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
|
||||
daten_bsl=formr_api_results(
|
||||
run_name = "BSL",
|
||||
"bsl",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_desii=formr_api_results(
|
||||
run_name = "DES-II",
|
||||
"desii",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_bai=formr_api_results(
|
||||
run_name = "BAI",
|
||||
"bai",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_pds5=formr_api_results(
|
||||
run_name = "PDS-5",
|
||||
"pds5",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
daten_mkf30=formr_api_results(
|
||||
run_name = "MKF-30",
|
||||
"mkf30",
|
||||
compute_scales = FALSE,
|
||||
join = TRUE,
|
||||
remove_test_sessions = FALSE,
|
||||
verbose = FALSE
|
||||
)
|
||||
|
||||
|
||||
formr_api_logout(verbose = FALSE)
|
||||
35
#alt_skripte/inject_chiffre_param.sh
Normal file
35
#alt_skripte/inject_chiffre_param.sh
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#!/usr/bin/env bash
|
||||
# Injiziert den URL-Parameter-Block (Chiffre aus ?chiffre=... vorausfuellen)
|
||||
# in alle app.R-Dateien neben diesem Skript (bzw. im Root, ausgefuehrt dort).
|
||||
# Idempotent: bereits gepatchte Dateien werden uebersprungen. Legt vor jeder
|
||||
# Aenderung ein .bak an.
|
||||
set -euo pipefail
|
||||
|
||||
SNIPPET=' 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)))
|
||||
}
|
||||
})'
|
||||
|
||||
MARKER='parseQueryString(session$clientData$url_search)'
|
||||
|
||||
find . -maxdepth 2 -iname 'app.R' -print0 | while IFS= read -r -d '' f; do
|
||||
if grep -qF "$MARKER" "$f"; then
|
||||
echo "SKIP (bereits vorhanden): $f"
|
||||
continue
|
||||
fi
|
||||
if ! grep -qE 'server[[:space:]]*=[[:space:]]*function\(input, output, session\)[[:space:]]*\{' "$f"; then
|
||||
echo "WARNUNG: Server-Signatur nicht gefunden, uebersprungen: $f"
|
||||
continue
|
||||
fi
|
||||
cp "$f" "$f.bak"
|
||||
awk -v snippet="$SNIPPET" '
|
||||
{ print }
|
||||
/server[ \t]*=[ \t]*function\(input, output, session\)[ \t]*\{/ && !done {
|
||||
print snippet
|
||||
done = 1
|
||||
}
|
||||
' "$f.bak" > "$f"
|
||||
echo "OK: $f (Backup: $f.bak)"
|
||||
done
|
||||
267
#alt_skripte/inject_pseudonym.py
Normal file
267
#alt_skripte/inject_pseudonym.py
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Injects pseudonym-based matching into every Diagnostik-App Shiny app.R.
|
||||
|
||||
Usage (run from the "formr" folder that directly contains all the app
|
||||
subfolders, next to server.py / get_pseudo.R / pseudonyme.db):
|
||||
|
||||
python inject_pseudonym.py # dry run: reports what WOULD happen
|
||||
python inject_pseudonym.py --apply # actually writes the changes
|
||||
|
||||
Finds every "<app-folder>/app.R" itself (no shell globbing needed, so this
|
||||
works the same from Windows cmd.exe as from bash). Idempotent (marker-based -
|
||||
safe to rerun), makes a .bak next to each file it actually changes, and
|
||||
leaves a file completely untouched if any of the required anchors can't be
|
||||
found unambiguously - such files are reported with a reason instead of being
|
||||
guessed at.
|
||||
"""
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
MARKER = "# --- pseudonym-support-injection v1 ---"
|
||||
|
||||
|
||||
def inject(path, write=True):
|
||||
path = str(path)
|
||||
raw = open(path, encoding="utf-8", newline="").read()
|
||||
uses_crlf = raw.count("\r\n") > raw.count("\n") - raw.count("\r\n")
|
||||
text = raw.replace("\r\n", "\n")
|
||||
if MARKER in text:
|
||||
return "skip: already patched", text
|
||||
|
||||
orig = text
|
||||
warnings = []
|
||||
notes = []
|
||||
|
||||
# 1. UI field: a pseudonym textInput with a small italic hint instead of
|
||||
# a bulky bold label. If textInput("chiffre", ...) is directly preceded
|
||||
# by a simple single-purpose `div(style = "...",` wrapper (the common
|
||||
# shape), clone a sibling div so both fields sit side by side in the
|
||||
# same flex row instead of stacking. Otherwise fall back to a plain
|
||||
# inline sibling insert (functional, just possibly stacked) and note it.
|
||||
label_block = (
|
||||
'tagList(\n'
|
||||
'{i} "Pseudonym",\n'
|
||||
'{i} tags$span(style = "font-weight: normal; font-style: italic; '
|
||||
'font-size: 0.78em; color: #888; margin-left: 4px; white-space: nowrap;",\n'
|
||||
'{i} "optional, hat Vorrang vor Chiffre")\n'
|
||||
'{i} )'
|
||||
)
|
||||
|
||||
m_wrapped = re.search(
|
||||
r'^([ \t]*)div\(style\s*=\s*"[^"]*"\s*,\s*\n([ \t]*)textInput\("chiffre"',
|
||||
text, re.MULTILINE,
|
||||
)
|
||||
if m_wrapped:
|
||||
outer_indent, inner_indent = m_wrapped.group(1), m_wrapped.group(2)
|
||||
label = label_block.format(i=inner_indent)
|
||||
insertion = (
|
||||
f'{outer_indent}div(style = "min-width: 360px; white-space: nowrap;",\n'
|
||||
f'{inner_indent}textInput("pseudonym",\n'
|
||||
f'{inner_indent} label = {label},\n'
|
||||
f'{inner_indent} placeholder = "optional", width = "340px")\n'
|
||||
f'{outer_indent}),\n'
|
||||
)
|
||||
text = text[: m_wrapped.start()] + insertion + text[m_wrapped.start() :]
|
||||
else:
|
||||
m = re.search(r'^([ \t]*)textInput\("chiffre"', text, re.MULTILINE)
|
||||
if not m:
|
||||
warnings.append('UI: textInput("chiffre" not found')
|
||||
else:
|
||||
indent = m.group(1)
|
||||
label = label_block.format(i=indent)
|
||||
insertion = (
|
||||
f'{indent}textInput("pseudonym",\n'
|
||||
f'{indent} label = {label},\n'
|
||||
f'{indent} placeholder = "optional", width = "340px"),\n'
|
||||
)
|
||||
text = text[: m.start()] + insertion + text[m.start() :]
|
||||
notes.append(
|
||||
"UI: no simple preceding div(style=...) wrapper found around "
|
||||
"textInput(\"chiffre\" - pseudonym field inserted inline; layout "
|
||||
"may need a manual look (may stack instead of sitting side by side)"
|
||||
)
|
||||
|
||||
# 2. URL prefill: new observe() block right after the server function
|
||||
# signature, independent of/in addition to the existing chiffre one.
|
||||
m = re.search(r"server\s*=\s*function\(input,\s*output,\s*session\)\s*\{\n", text)
|
||||
if not m:
|
||||
warnings.append("server = function(input, output, session) { not found")
|
||||
else:
|
||||
block = (
|
||||
f" {MARKER}\n"
|
||||
" observe({\n"
|
||||
" query = parseQueryString(session$clientData$url_search)\n"
|
||||
" if (!is.null(query$pseudonym) && nchar(trimws(query$pseudonym)) > 0) {\n"
|
||||
' updateTextInput(session, "pseudonym", value = trimws(query$pseudonym))\n'
|
||||
" }\n"
|
||||
" })\n\n"
|
||||
)
|
||||
idx = m.end()
|
||||
text = text[:idx] + block + text[idx:]
|
||||
|
||||
# Some apps run their chiffre-empty/format checks inside a dedicated
|
||||
# top-level helper function (e.g. desii_validiere_chiffre(),
|
||||
# validiere_chiffre()) defined OUTSIDE server() - such a function has no
|
||||
# `input` in scope. Blindly rewriting a check there would compile fine
|
||||
# but throw "object 'input' not found" at runtime on every single click,
|
||||
# for every patient, whether pseudonym is used or not. So items 3 and 4
|
||||
# only ever touch occurrences that are reachable from inside server()
|
||||
# (i.e. at/after the server function's opening brace); anything found
|
||||
# only earlier in the file is left completely untouched and reported as
|
||||
# a note instead - pseudonym-only entry (no chiffre at all) just won't
|
||||
# work for that one app, but the chiffre+pseudonym disambiguation (the
|
||||
# actual bug this whole thing fixes) is unaffected either way.
|
||||
m_anchor = re.search(r"server\s*=\s*function\(input,\s*output,\s*session\)\s*\{", text)
|
||||
reactive_start = m_anchor.start() if m_anchor else None
|
||||
|
||||
def patch_reachable(text, target, replacement):
|
||||
matches = list(re.finditer(re.escape(target), text))
|
||||
reachable = [
|
||||
mm for mm in matches
|
||||
if reactive_start is not None and mm.start() >= reactive_start
|
||||
]
|
||||
for mm in reversed(reachable):
|
||||
text = text[: mm.start()] + replacement + text[mm.end() :]
|
||||
return text, len(matches), len(reachable)
|
||||
|
||||
# 3. Relax the "chiffre leer" gate - phrasing varies (some apps check
|
||||
# `chiffre`, others read into `chiffre_roh` first).
|
||||
empty_gate_variants = [
|
||||
("nchar(chiffre) == 0",
|
||||
"(nchar(trimws(input$pseudonym)) == 0 && nchar(chiffre) == 0)"),
|
||||
("nchar(trimws(chiffre_roh)) == 0",
|
||||
"(nchar(trimws(input$pseudonym)) == 0 && nchar(trimws(chiffre_roh)) == 0)"),
|
||||
('trimws(chiffre_roh) == ""',
|
||||
'(nchar(trimws(input$pseudonym)) == 0 && trimws(chiffre_roh) == "")'),
|
||||
]
|
||||
any_empty_gate_patched = False
|
||||
for target, replacement in empty_gate_variants:
|
||||
text, total, reachable = patch_reachable(text, target, replacement)
|
||||
if reachable:
|
||||
any_empty_gate_patched = True
|
||||
if not any_empty_gate_patched:
|
||||
notes.append(
|
||||
"no reachable chiffre-empty gate found (the format-check gate below "
|
||||
"usually already covers an empty chiffre too, so this is often fine)"
|
||||
)
|
||||
|
||||
# 4. Relax the chiffre-format gate.
|
||||
target = 'grepl("^[A-Z][0-9]{6}$", chiffre)'
|
||||
replacement = '(nchar(trimws(input$pseudonym)) > 0 || grepl("^[A-Z][0-9]{6}$", chiffre))'
|
||||
text, total, reachable = patch_reachable(text, target, replacement)
|
||||
if reachable == 0:
|
||||
if total > 0:
|
||||
notes.append(
|
||||
"chiffre-format check only exists inside a top-level helper function "
|
||||
"with no access to the pseudonym input - pseudonym-only entry (no "
|
||||
"chiffre at all) won't work for this app, but chiffre+pseudonym "
|
||||
"disambiguation (the main fix) is unaffected"
|
||||
)
|
||||
else:
|
||||
warnings.append(f'gate not found: "{target}"')
|
||||
|
||||
# 5. Backfill `chiffre` from the pseudonym right after the pseudonym
|
||||
# script is sourced (pseudo now exists in .GlobalEnv - guaranteed
|
||||
# name, set by the one shared get_pseudo.R).
|
||||
target = "source(PFAD_PSEUDONYM_SKRIPT, local = FALSE)"
|
||||
occurrences = [mm.start() for mm in re.finditer(re.escape(target), text)]
|
||||
if len(occurrences) != 1:
|
||||
warnings.append(
|
||||
f'expected exactly 1 occurrence of source(PFAD_PSEUDONYM_SKRIPT...), found {len(occurrences)}'
|
||||
)
|
||||
else:
|
||||
pos = occurrences[0]
|
||||
line_start = text.rfind("\n", 0, pos) + 1
|
||||
indent = re.match(r"[ \t]*", text[line_start:pos]).group(0)
|
||||
end = pos + len(target)
|
||||
backfill = (
|
||||
f"\n{indent}if (nchar(trimws(input$pseudonym)) > 0) {{\n"
|
||||
f'{indent} .pw_wert = trimws(input$pseudonym)\n'
|
||||
f'{indent} .pw_tab = get("pseudo", envir = .GlobalEnv)\n'
|
||||
f"{indent} .pw_treffer = .pw_tab[.pw_tab$pseudonym == .pw_wert, ]\n"
|
||||
f"{indent} if (nrow(.pw_treffer) > 0) chiffre = toupper(trimws(.pw_treffer$chiffre[1]))\n"
|
||||
f"{indent}}}"
|
||||
)
|
||||
text = text[:end] + backfill + text[end:]
|
||||
|
||||
# 6. Override the final session-id(s) with the exact pseudonym, so an
|
||||
# ambiguous chiffre (multiple pseudonyms) doesn't silently pick the
|
||||
# wrong / most-recent entry.
|
||||
sid_pattern = re.compile(
|
||||
r"^([ \t]*)([A-Za-z_][A-Za-z0-9_.]*)\s*=\s*"
|
||||
r"(unique\(as\.character\(\w+\$pseudonym\)\)|unique\(\w+\$pseudonym\)|\w+\$pseudonym\[1\])[ \t]*$",
|
||||
re.MULTILINE,
|
||||
)
|
||||
matches = list(sid_pattern.finditer(text))
|
||||
if len(matches) == 0:
|
||||
warnings.append("session-id assignment (X = unique(Y$pseudonym) / Y$pseudonym[1]) not found")
|
||||
else:
|
||||
for mm in reversed(matches):
|
||||
indent, varname = mm.group(1), mm.group(2)
|
||||
override = f"\n{indent}if (nchar(trimws(input$pseudonym)) > 0) {varname} = trimws(input$pseudonym)"
|
||||
text = text[: mm.end()] + override + text[mm.end() :]
|
||||
|
||||
if warnings:
|
||||
return "skip: " + "; ".join(warnings), orig
|
||||
|
||||
out = text.replace("\n", "\r\n") if uses_crlf else text
|
||||
|
||||
if write:
|
||||
shutil.copy(path, path + ".bak")
|
||||
with open(path, "w", encoding="utf-8", newline="") as f:
|
||||
f.write(out)
|
||||
|
||||
status = "patched"
|
||||
if notes:
|
||||
status += " (note: " + "; ".join(notes) + ")"
|
||||
return status, out
|
||||
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
apply_changes = "--apply" in args
|
||||
args = [a for a in args if a != "--apply"]
|
||||
|
||||
if args:
|
||||
paths = [Path(a) for a in args]
|
||||
else:
|
||||
paths = sorted(Path(".").glob("*/app.R"))
|
||||
|
||||
if not paths:
|
||||
print('No "*/app.R" files found in the current directory. '
|
||||
"Run this from the folder that directly contains all the app "
|
||||
"subfolders (next to server.py / get_pseudo.R / pseudonyme.db), "
|
||||
"or pass explicit paths.")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"{'DRY RUN (no files will be changed - pass --apply to write)' if not apply_changes else 'APPLYING CHANGES'}")
|
||||
print(f"{len(paths)} app(s) found.\n")
|
||||
|
||||
counts = {"patched": 0, "patched-with-note": 0, "skip-already-patched": 0, "skip": 0}
|
||||
for p in paths:
|
||||
try:
|
||||
status, _ = inject(p, write=apply_changes)
|
||||
except Exception as e:
|
||||
status = f"skip: UNEXPECTED ERROR ({type(e).__name__}: {e}) - file left untouched, please report this"
|
||||
if status.startswith("skip: already patched"):
|
||||
counts["skip-already-patched"] += 1
|
||||
elif status.startswith("skip:"):
|
||||
counts["skip"] += 1
|
||||
elif "(note:" in status:
|
||||
counts["patched-with-note"] += 1
|
||||
else:
|
||||
counts["patched"] += 1
|
||||
print(f"{p}: {status}")
|
||||
|
||||
print(
|
||||
f"\n{counts['patched']} clean, {counts['patched-with-note']} patched-with-note, "
|
||||
f"{counts['skip']} skipped, {counts['skip-already-patched']} already patched."
|
||||
)
|
||||
if not apply_changes:
|
||||
print("This was a dry run - nothing was written. Rerun with --apply once you're happy.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
40
#alt_skripte/post_new_user.R
Normal file
40
#alt_skripte/post_new_user.R
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
library(formr)
|
||||
|
||||
formr_store_keys(host = "https://diagnostik.karneboge.de/api", client_id = "6be1cb43d9964516089238a292b30098", client_secret = "e10c32d15c5ad9ecf44dc17d3d997fd3e7fc4c4153478fec5006904a7056f02e")
|
||||
formr_api_authenticate(host = "https://diagnostik.karneboge.de/api")
|
||||
|
||||
|
||||
|
||||
pseudoID="4V8QodhlrGKifPKRdSpSFj-OFC4etlCvaa4GCQ1FAXE_PjDE7UdBCynDVa29guRu"
|
||||
|
||||
|
||||
|
||||
|
||||
#SKID 5 SPQ ####
|
||||
formr_api_create_session(
|
||||
"SKID5-SPQ",
|
||||
codes = pseudoID,
|
||||
testing = FALSE,
|
||||
verbose = TRUE
|
||||
)
|
||||
formr_api_logout(verbose = FALSE)
|
||||
|
||||
as.data.frame(
|
||||
formr_api_sessions(
|
||||
"SKID5-SPQ",
|
||||
session_codes = pseudoID,
|
||||
active = NULL,
|
||||
testing = NULL,
|
||||
limit = 1000,
|
||||
offset = 0,
|
||||
verbose = TRUE
|
||||
)
|
||||
)
|
||||
#BDI2 ####
|
||||
formr_api_create_session(
|
||||
"BDI2",
|
||||
codes = pseudoID,
|
||||
testing = FALSE,
|
||||
verbose = TRUE
|
||||
)
|
||||
formr_api_logout(verbose = FALSE)
|
||||
66
#alt_skripte/resore_renv.ps1
Normal file
66
#alt_skripte/resore_renv.ps1
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Basisordner, in dem alle Shiny App Unterordner liegen
|
||||
$baseDir = "C:\Users\johnn\Desktop\formr"
|
||||
|
||||
# Voller Pfad zu Rscript.exe
|
||||
$rscriptExe = "C:\Program Files\R\R-4.6.0\bin\x64\Rscript.exe"
|
||||
|
||||
# Logdatei für die Ergebnisse
|
||||
$logFile = Join-Path $baseDir "renv_restore_log.txt"
|
||||
"Start: $(Get-Date)" | Out-File -FilePath $logFile
|
||||
|
||||
# Alle Unterordner finden, die eine renv.lock enthalten
|
||||
$appFolders = Get-ChildItem -Path $baseDir -Recurse -Filter "renv.lock" |
|
||||
ForEach-Object { $_.Directory.FullName } |
|
||||
Sort-Object -Unique
|
||||
|
||||
Write-Host "Gefundene Apps mit renv.lock: $($appFolders.Count)"
|
||||
|
||||
$erfolgreich = @()
|
||||
$fehlgeschlagen = @()
|
||||
|
||||
foreach ($folder in $appFolders) {
|
||||
Write-Host "Bearbeite: $folder"
|
||||
"----" | Out-File -FilePath $logFile -Append
|
||||
"App: $folder" | Out-File -FilePath $logFile -Append
|
||||
|
||||
# R Befehl als Einzeiler, escaped für Rscript -e
|
||||
$rCommand = "renv::restore(project = '$($folder -replace '\\','/')', prompt = FALSE)"
|
||||
|
||||
$output = & $rscriptExe -e $rCommand 2>&1
|
||||
$output | Out-File -FilePath $logFile -Append
|
||||
|
||||
# Grobe Erfolgsprüfung: enthält die Ausgabe Fehlerhinweise?
|
||||
$hatFehler = $output -match "Error|error in|konnte nicht|es gibt kein Paket"
|
||||
|
||||
if ($hatFehler) {
|
||||
"Status: FEHLER" | Out-File -FilePath $logFile -Append
|
||||
$fehlgeschlagen += $folder
|
||||
} else {
|
||||
"Status: OK" | Out-File -FilePath $logFile -Append
|
||||
$erfolgreich += $folder
|
||||
}
|
||||
}
|
||||
|
||||
"Ende: $(Get-Date)" | Out-File -FilePath $logFile -Append
|
||||
|
||||
# Zusammenfassung
|
||||
Write-Host ""
|
||||
Write-Host "===== Zusammenfassung ====="
|
||||
Write-Host "Gesamt: $($appFolders.Count)"
|
||||
Write-Host "Erfolgreich: $($erfolgreich.Count)"
|
||||
Write-Host "Fehlgeschlagen: $($fehlgeschlagen.Count)"
|
||||
|
||||
if ($fehlgeschlagen.Count -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Host "Fehlgeschlagene Ordner:"
|
||||
$fehlgeschlagen | ForEach-Object { Write-Host " - $_" }
|
||||
}
|
||||
|
||||
"===== Zusammenfassung =====" | Out-File -FilePath $logFile -Append
|
||||
"Gesamt: $($appFolders.Count)" | Out-File -FilePath $logFile -Append
|
||||
"Erfolgreich: $($erfolgreich.Count)" | Out-File -FilePath $logFile -Append
|
||||
"Fehlgeschlagen: $($fehlgeschlagen.Count)" | Out-File -FilePath $logFile -Append
|
||||
$fehlgeschlagen | Out-File -FilePath $logFile -Append
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Fertig. Log unter: $logFile"
|
||||
461
#alt_skripte/update-renv-projects-v7.ps1
Normal file
461
#alt_skripte/update-renv-projects-v7.ps1
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
[CmdletBinding()]
|
||||
param(
|
||||
[ValidateSet("Status", "Restore", "Snapshot", "SyncToLibrary")]
|
||||
[string]$Mode = "Status",
|
||||
|
||||
[string]$Root = "C:\Diagnostik\formr",
|
||||
|
||||
[string]$Rscript = "C:\Program Files\R\R-4.6.0\bin\x64\Rscript.exe",
|
||||
|
||||
[switch]$IncludeNestedProjects
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Write-Log {
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[AllowEmptyString()]
|
||||
[string]$Message,
|
||||
|
||||
[switch]$Initialize
|
||||
)
|
||||
|
||||
$maxAttempts = 10
|
||||
|
||||
for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) {
|
||||
try {
|
||||
if ($Initialize) {
|
||||
Set-Content -LiteralPath $logFile -Value $Message -Encoding UTF8 -ErrorAction Stop
|
||||
}
|
||||
else {
|
||||
Add-Content -LiteralPath $logFile -Value $Message -Encoding UTF8 -ErrorAction Stop
|
||||
}
|
||||
return
|
||||
}
|
||||
catch [System.IO.IOException] {
|
||||
if ($attempt -eq $maxAttempts) {
|
||||
Write-Warning "Logdatei konnte nach $maxAttempts Versuchen nicht beschrieben werden: $logFile"
|
||||
Write-Warning $_.Exception.Message
|
||||
return
|
||||
}
|
||||
|
||||
Start-Sleep -Milliseconds (100 * $attempt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||||
$logFile = Join-Path $Root "renv_${Mode}_${timestamp}_PID${PID}.log"
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Rscript -PathType Leaf)) {
|
||||
throw "Rscript wurde nicht gefunden: $Rscript"
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Root -PathType Container)) {
|
||||
throw "Stammverzeichnis wurde nicht gefunden: $Root"
|
||||
}
|
||||
|
||||
function ConvertTo-RString {
|
||||
param([Parameter(Mandatory)][string]$Value)
|
||||
|
||||
return $Value.Replace("\", "/").Replace("'", "\'")
|
||||
}
|
||||
|
||||
function Invoke-RenvProject {
|
||||
param(
|
||||
[Parameter(Mandatory)][string]$Project,
|
||||
[Parameter(Mandatory)][string]$Action
|
||||
)
|
||||
|
||||
$projectR = ConvertTo-RString -Value $Project
|
||||
|
||||
$actionCode = switch ($Action) {
|
||||
"Status" {
|
||||
@'
|
||||
# Nur prüfen
|
||||
'@
|
||||
}
|
||||
"Restore" {
|
||||
@'
|
||||
renv::restore(project = project, prompt = FALSE)
|
||||
'@
|
||||
}
|
||||
"Snapshot" {
|
||||
@'
|
||||
lockfile <- file.path(project, "renv.lock")
|
||||
backup <- paste0(lockfile, ".bak_", format(Sys.time(), "%Y%m%d_%H%M%S"))
|
||||
if (file.exists(lockfile)) {
|
||||
file.copy(lockfile, backup, overwrite = FALSE)
|
||||
message("Lockfile-Backup: ", backup)
|
||||
}
|
||||
renv::snapshot(project = project, prompt = FALSE)
|
||||
'@
|
||||
}
|
||||
"SyncToLibrary" {
|
||||
@'
|
||||
initial_status <- renv::status(project = project)
|
||||
|
||||
if (isTRUE(initial_status$synchronized)) {
|
||||
message("Projekt ist bereits synchron; keine Änderung.")
|
||||
} else {
|
||||
library_path <- renv::paths$library(project = project)
|
||||
|
||||
# Unvollständige Paketordner und Installationsreste entfernen.
|
||||
if (dir.exists(library_path)) {
|
||||
entries <- list.dirs(
|
||||
library_path,
|
||||
full.names = TRUE,
|
||||
recursive = FALSE
|
||||
)
|
||||
|
||||
invalid <- entries[
|
||||
grepl("^00LOCK", basename(entries)) |
|
||||
!file.exists(file.path(entries, "DESCRIPTION"))
|
||||
]
|
||||
|
||||
invalid <- unique(invalid)
|
||||
|
||||
if (length(invalid)) {
|
||||
message(
|
||||
"Entferne unvollständige Paketordner: ",
|
||||
paste(basename(invalid), collapse = ", ")
|
||||
)
|
||||
|
||||
unlink(
|
||||
invalid,
|
||||
recursive = TRUE,
|
||||
force = TRUE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
# Direkte Projektabhängigkeiten aus dem Code.
|
||||
dependencies <- renv::dependencies(
|
||||
path = project,
|
||||
progress = FALSE,
|
||||
errors = "reported"
|
||||
)
|
||||
|
||||
direct_packages <- sort(
|
||||
unique(stats::na.omit(dependencies$Package))
|
||||
)
|
||||
|
||||
# Zusätzlich alle im Lockfile dokumentierten Pakete berücksichtigen.
|
||||
lockfile_path <- file.path(project, "renv.lock")
|
||||
lock_packages <- character()
|
||||
|
||||
if (file.exists(lockfile_path)) {
|
||||
lock <- renv::lockfile_read(lockfile_path)
|
||||
lock_packages <- names(lock$Packages)
|
||||
}
|
||||
|
||||
base_packages <- rownames(
|
||||
installed.packages(priority = "base")
|
||||
)
|
||||
|
||||
target_packages <- sort(unique(c(
|
||||
direct_packages,
|
||||
lock_packages
|
||||
)))
|
||||
|
||||
target_packages <- setdiff(
|
||||
target_packages,
|
||||
base_packages
|
||||
)
|
||||
|
||||
valid_installed_packages <- function() {
|
||||
if (!dir.exists(library_path)) {
|
||||
return(character())
|
||||
}
|
||||
|
||||
dirs <- list.dirs(
|
||||
library_path,
|
||||
full.names = TRUE,
|
||||
recursive = FALSE
|
||||
)
|
||||
|
||||
basename(
|
||||
dirs[file.exists(file.path(dirs, "DESCRIPTION"))]
|
||||
)
|
||||
}
|
||||
|
||||
missing <- setdiff(
|
||||
target_packages,
|
||||
valid_installed_packages()
|
||||
)
|
||||
|
||||
# Fehlende Pakete einzeln installieren. Fehlgeschlagene Pakete werden
|
||||
# mehrfach versucht, damit zuerst installierte Abhängigkeiten später
|
||||
# abhängige Pakete ermöglichen.
|
||||
max_passes <- 3L
|
||||
failed <- missing
|
||||
|
||||
if (length(failed)) {
|
||||
for (pass in seq_len(max_passes)) {
|
||||
message(
|
||||
"Installationsdurchgang ",
|
||||
pass,
|
||||
" von ",
|
||||
max_passes,
|
||||
": ",
|
||||
paste(failed, collapse = ", ")
|
||||
)
|
||||
|
||||
next_failed <- character()
|
||||
|
||||
for (package in failed) {
|
||||
message("Installiere Paket: ", package)
|
||||
|
||||
ok <- tryCatch(
|
||||
{
|
||||
renv::install(
|
||||
packages = package,
|
||||
project = project,
|
||||
rebuild = TRUE,
|
||||
prompt = FALSE
|
||||
)
|
||||
TRUE
|
||||
},
|
||||
error = function(error) {
|
||||
message(
|
||||
"Installation fehlgeschlagen für ",
|
||||
package,
|
||||
": ",
|
||||
conditionMessage(error)
|
||||
)
|
||||
FALSE
|
||||
}
|
||||
)
|
||||
|
||||
if (!isTRUE(ok)) {
|
||||
next_failed <- c(next_failed, package)
|
||||
}
|
||||
}
|
||||
|
||||
failed <- unique(next_failed)
|
||||
|
||||
# Pakete können als Abhängigkeiten anderer Installationen
|
||||
# erfolgreich hinzugekommen sein.
|
||||
failed <- setdiff(
|
||||
failed,
|
||||
valid_installed_packages()
|
||||
)
|
||||
|
||||
if (!length(failed)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
still_missing <- setdiff(
|
||||
target_packages,
|
||||
valid_installed_packages()
|
||||
)
|
||||
|
||||
if (length(still_missing)) {
|
||||
stop(
|
||||
"Nach mehreren Installationsdurchgängen fehlen weiterhin: ",
|
||||
paste(still_missing, collapse = ", "),
|
||||
". Kein Snapshot durchgeführt."
|
||||
)
|
||||
}
|
||||
|
||||
# Vor dem Snapshot Lockfile sichern.
|
||||
backup <- paste0(
|
||||
lockfile_path,
|
||||
".bak_",
|
||||
format(Sys.time(), "%Y%m%d_%H%M%S")
|
||||
)
|
||||
|
||||
if (file.exists(lockfile_path)) {
|
||||
copied <- file.copy(
|
||||
lockfile_path,
|
||||
backup,
|
||||
overwrite = FALSE
|
||||
)
|
||||
|
||||
if (!isTRUE(copied)) {
|
||||
stop(
|
||||
"Lockfile-Backup konnte nicht erstellt werden: ",
|
||||
backup
|
||||
)
|
||||
}
|
||||
|
||||
message("Lockfile-Backup: ", backup)
|
||||
}
|
||||
|
||||
# Snapshot führt zusätzlich eine transitive Vorabvalidierung durch.
|
||||
# Bei einem Fehler bleibt das Backup erhalten und das Projekt wird
|
||||
# nicht als erfolgreich markiert.
|
||||
message("Aktualisiere renv.lock.")
|
||||
renv::snapshot(
|
||||
project = project,
|
||||
prompt = FALSE
|
||||
)
|
||||
}
|
||||
'@
|
||||
}
|
||||
}
|
||||
|
||||
$rCode = @"
|
||||
options(
|
||||
repos = c(CRAN = "https://cloud.r-project.org"),
|
||||
renv.config.auto.snapshot = FALSE
|
||||
)
|
||||
|
||||
project <- '$projectR'
|
||||
message("Projekt: ", project)
|
||||
message("R-Version: ", R.version.string)
|
||||
|
||||
$actionCode
|
||||
|
||||
status <- renv::status(project = project)
|
||||
if (!isTRUE(status`$synchronized)) {
|
||||
message("ERGEBNIS: NICHT SYNCHRON")
|
||||
quit(save = "no", status = 10L)
|
||||
}
|
||||
|
||||
message("ERGEBNIS: SYNCHRON")
|
||||
quit(save = "no", status = 0L)
|
||||
"@
|
||||
|
||||
$tempRFile = Join-Path ([System.IO.Path]::GetTempPath()) (
|
||||
"renv_{0}_{1}.R" -f ([System.IO.Path]::GetRandomFileName()), $PID
|
||||
)
|
||||
|
||||
try {
|
||||
# R-Code nicht mit -e übergeben. Unter Windows können dabei Anführungszeichen
|
||||
# verloren gehen, sodass z. B. C:/Users/... als ungequoteter R-Code ankommt.
|
||||
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)
|
||||
[System.IO.File]::WriteAllText($tempRFile, $rCode, $utf8NoBom)
|
||||
|
||||
$stdoutFile = Join-Path ([System.IO.Path]::GetTempPath()) (
|
||||
"renv_stdout_{0}_{1}.txt" -f ([System.IO.Path]::GetRandomFileName()), $PID
|
||||
)
|
||||
$stderrFile = Join-Path ([System.IO.Path]::GetTempPath()) (
|
||||
"renv_stderr_{0}_{1}.txt" -f ([System.IO.Path]::GetRandomFileName()), $PID
|
||||
)
|
||||
|
||||
Push-Location -LiteralPath $Project
|
||||
try {
|
||||
$process = Start-Process `
|
||||
-FilePath $Rscript `
|
||||
-ArgumentList @("--no-save", "--no-restore", $tempRFile) `
|
||||
-WorkingDirectory $Project `
|
||||
-RedirectStandardOutput $stdoutFile `
|
||||
-RedirectStandardError $stderrFile `
|
||||
-NoNewWindow `
|
||||
-Wait `
|
||||
-PassThru
|
||||
|
||||
$exitCode = $process.ExitCode
|
||||
|
||||
$output = @()
|
||||
if (Test-Path -LiteralPath $stdoutFile) {
|
||||
$output += Get-Content -LiteralPath $stdoutFile -ErrorAction SilentlyContinue
|
||||
}
|
||||
if (Test-Path -LiteralPath $stderrFile) {
|
||||
$output += Get-Content -LiteralPath $stderrFile -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Pop-Location
|
||||
Remove-Item -LiteralPath $stdoutFile -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -LiteralPath $stderrFile -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Remove-Item -LiteralPath $tempRFile -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
foreach ($line in $output) {
|
||||
$text = [string]$line
|
||||
Write-Host $text
|
||||
Write-Log -Message $text
|
||||
}
|
||||
|
||||
return $exitCode
|
||||
}
|
||||
|
||||
$searchDepth = if ($IncludeNestedProjects) { 20 } else { 1 }
|
||||
|
||||
$projects = Get-ChildItem -LiteralPath $Root -Directory |
|
||||
Where-Object {
|
||||
Test-Path -LiteralPath (Join-Path $_.FullName "renv.lock") -PathType Leaf
|
||||
}
|
||||
|
||||
if ($IncludeNestedProjects) {
|
||||
$projects = Get-ChildItem -LiteralPath $Root -Directory -Recurse -Depth $searchDepth |
|
||||
Where-Object {
|
||||
Test-Path -LiteralPath (Join-Path $_.FullName "renv.lock") -PathType Leaf
|
||||
}
|
||||
}
|
||||
|
||||
$projects = $projects | Sort-Object FullName -Unique
|
||||
|
||||
if (-not $projects) {
|
||||
throw "Keine Projekte mit renv.lock unter '$Root' gefunden."
|
||||
}
|
||||
|
||||
Write-Log -Message "Start: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -Initialize
|
||||
Write-Log -Message "Modus: $Mode"
|
||||
Write-Log -Message "Rscript: $Rscript"
|
||||
Write-Log -Message "Projekte: $($projects.Count)"
|
||||
|
||||
$results = foreach ($project in $projects) {
|
||||
Write-Host ""
|
||||
Write-Host ("=" * 78)
|
||||
Write-Host "[$Mode] $($project.FullName)"
|
||||
Write-Host ("=" * 78)
|
||||
|
||||
Write-Log -Message "`r`n[$Mode] $($project.FullName)"
|
||||
|
||||
try {
|
||||
$code = Invoke-RenvProject -Project $project.FullName -Action $Mode
|
||||
|
||||
[pscustomobject]@{
|
||||
Project = $project.Name
|
||||
Path = $project.FullName
|
||||
Result = switch ($code) {
|
||||
0 { "Synchron" }
|
||||
10 { "Nicht synchron" }
|
||||
default { "Fehler, Exitcode $code" }
|
||||
}
|
||||
ExitCode = $code
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$message = $_.Exception.Message
|
||||
Write-Warning $message
|
||||
Write-Log -Message "FEHLER: $message"
|
||||
|
||||
[pscustomobject]@{
|
||||
Project = $project.Name
|
||||
Path = $project.FullName
|
||||
Result = "PowerShell-Fehler"
|
||||
ExitCode = 99
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Zusammenfassung"
|
||||
$results | Format-Table Project, Result, ExitCode -AutoSize
|
||||
|
||||
$csvFile = [System.IO.Path]::ChangeExtension($logFile, ".csv")
|
||||
$results | Export-Csv -LiteralPath $csvFile -NoTypeInformation -Encoding UTF8
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Log: $logFile"
|
||||
Write-Host "CSV: $csvFile"
|
||||
|
||||
if ($results.ExitCode -contains 99 -or ($results.ExitCode | Where-Object { $_ -notin 0, 10 })) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($results.ExitCode -contains 10) {
|
||||
exit 10
|
||||
}
|
||||
|
||||
exit 0
|
||||
6
#alt_skripte/upload_files.R
Normal file
6
#alt_skripte/upload_files.R
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
library(formr)
|
||||
formr_store_keys(host = "https://diagnostik.karneboge.de/api", client_id = "be81e57d26aa5da69e235ff6d04e7cdd", client_secret = "a88f352517f2f49d840e927cb3a92aa4c22031310d092630db9bf57f8fdf25e1")
|
||||
formr_api_authenticate(host = "https://diagnostik.karneboge.de/api")
|
||||
|
||||
formr_api_upload_file("IST-Screening", "./Body_Female.png", verbose = TRUE)
|
||||
formr_api_files("IST-Screening", verbose = TRUE)
|
||||
Loading…
Add table
Add a link
Reference in a new issue