commit e805f225bc3d03016116d227f1a0886b39b640af Author: root Date: Tue Mar 24 10:22:01 2026 +0000 initial upload diff --git a/change_password.php b/change_password.php new file mode 100644 index 0000000..d80f90d --- /dev/null +++ b/change_password.php @@ -0,0 +1,97 @@ + 'pw1', + 'user02' => 'pw2', + 'Tmp_Daniel' => 'dummy4', + 'Tmp_Johanna' => 'dummy1', + 'Tmp_Anke' => 'dummy2', + 'Tmp_Liliana' => 'dummy3', + 'Amy_tmp' => 'dummy4', + 'Brigitte_tmp' => 'dummy5', + 'Extra1_tmp' => 'dummy6', + 'Extra2_tmp' => 'dummy7', + 'Danny' => 'pw1', + 'User1' => 'pw1', + 'User2' => 'pw2', + 'User3' => 'pw3', + 'User4' => 'pw4', + 'User5' => 'pw5', + 'User6' => 'pw6', + 'User7' => 'pw7', + 'User8' => 'pw8', + 'User9' => 'pw9', + 'User10' => 'pw10', + 'User11' => 'pw11' +]; + +$STORE_FILE = __DIR__ . '/users.json'; + +function load_store($path) { + if (!file_exists($path)) return []; + $txt = file_get_contents($path); + $arr = json_decode($txt, true); + return is_array($arr) ? $arr : []; +} +function save_store($path, $data) { + file_put_contents($path, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); +} + +$raw = file_get_contents("php://input"); +$in = json_decode($raw, true) ?: []; + +$username = trim($in['username'] ?? ''); +$oldPassword = (string)($in['old_password'] ?? ''); +$newPassword = (string)($in['new_password'] ?? ''); + +if ($username === '' || $oldPassword === '' || $newPassword === '') { + http_response_code(400); + echo json_encode(["success" => false, "message" => "Felder fehlen"]); + exit; +} +if (strlen($newPassword) < 6) { + http_response_code(400); + echo json_encode(["success" => false, "message" => "Neues Passwort zu kurz (min. 6 Zeichen)."]); + exit; +} + +$store = load_store($STORE_FILE); + +// Fall A: User hat schon Hash -> altes Passwort gegen Hash prüfen +if (array_key_exists($username, $store) && isset($store[$username]['hash'])) { + $hash = (string)$store[$username]['hash']; + if (!password_verify($oldPassword, $hash)) { + http_response_code(401); + echo json_encode(["success" => false, "message" => "Altes Passwort falsch."]); + exit; + } +} else { + // Fall B: User hat noch keinen Hash -> gegen Default prüfen + if (!array_key_exists($username, $USERS_DEFAULT) || $USERS_DEFAULT[$username] !== $oldPassword) { + http_response_code(401); + echo json_encode(["success" => false, "message" => "Altes Passwort falsch."]); + exit; + } +} + +// neuen Hash setzen (bcrypt) +$store[$username] = [ + "hash" => password_hash($newPassword, PASSWORD_DEFAULT), + "changedAt" => time() +]; +save_store($STORE_FILE, $store); + +// Nach erfolgreichem Wechsel direkt einloggen (Token erstellen) +$token = bin2hex(random_bytes(32)); +token_add($token, 30 * 24 * 60 * 60); + +echo json_encode([ + "success" => true, + "message" => "Passwort geändert.", + "token" => $token, + "user" => $username +]); diff --git a/checkDatabaseExists.php b/checkDatabaseExists.php new file mode 100755 index 0000000..efee289 --- /dev/null +++ b/checkDatabaseExists.php @@ -0,0 +1,10 @@ + true]); +} else { + echo json_encode(["exists" => false]); +} diff --git a/common.php b/common.php new file mode 100644 index 0000000..db19362 --- /dev/null +++ b/common.php @@ -0,0 +1,91 @@ + 0) { + return str_pad(substr($b, 0, 32), 32, "\0"); + } + return str_pad(substr($env, 0, 32), 32, "\0"); + } + // Fallback: alter Key aus bestehender Installation (32 ASCII-Bytes) + return "12345678901234567890123456789012"; +} + +// --- HKDF (SHA-256) über Token -> Session-Key (32 Byte) --- +// Kompatibel zu Android-Implementierung: salt = leer, info = "qdb-aes" +function hkdf_session_key_from_token(string $tokenHex, string $info = 'qdb-aes', int $len = 32): string { + $ikm = @hex2bin(trim($tokenHex)); + if ($ikm === false) $ikm = $tokenHex; // Falls bereits binär + if (function_exists('hash_hkdf')) { + return hash_hkdf('sha256', $ikm, $len, $info, ''); + } + // Fallback: eigene HKDF-Implementierung + $hashLen = 32; + $salt = str_repeat("\0", $hashLen); + $prk = hash_hmac('sha256', $ikm, $salt, true); + $okm = ''; + $t = ''; + $n = (int)ceil($len / $hashLen); + for ($i = 1; $i <= $n; $i++) { + $t = hash_hmac('sha256', $t . $info . chr($i), $prk, true); + $okm .= $t; + } + return substr($okm, 0, $len); +} + +// --- Token-Storage (mit Ablauf) --- +function tokens_file_path(): string { + return __DIR__ . '/tokens.jsonl'; +} +function token_add(string $token, int $ttlSeconds = 86400): void { + $rec = [ + 'token' => $token, + 'created' => time(), + 'exp' => time() + $ttlSeconds, + ]; + file_put_contents(tokens_file_path(), json_encode($rec) . PHP_EOL, FILE_APPEND | LOCK_EX); + // optional weiterhin für Alt-Skripte: + file_put_contents(__DIR__ . '/valid_tokens.txt', $token . PHP_EOL, FILE_APPEND | LOCK_EX); +} +function token_is_valid(string $token): bool { + $f = tokens_file_path(); + if (!file_exists($f)) return false; + $h = fopen($f, 'r'); + if (!$h) return false; + $now = time(); + $valid = false; + while (($line = fgets($h)) !== false) { + $j = json_decode($line, true); + if (!is_array($j)) continue; + if (($j['token'] ?? '') === $token && ($j['exp'] ?? 0) >= $now) { + $valid = true; break; + } + } + fclose($h); + return $valid; +} + +// --- AES-256-CBC: IV(16) + CIPHERTEXT --- +function aes256_cbc_encrypt_bytes(string $plain, string $key): string { + $key = str_pad(substr($key, 0, 32), 32, "\0"); + $iv = random_bytes(16); + $cipher = openssl_encrypt($plain, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv); + if ($cipher === false) throw new Exception('openssl_encrypt failed'); + return $iv . $cipher; +} +function aes256_cbc_decrypt_bytes(string $data, string $key): string { + if (strlen($data) < 16) throw new Exception('cipher too short'); + $key = str_pad(substr($key, 0, 32), 32, "\0"); + $iv = substr($data, 0, 16); + $ct = substr($data, 16); + $plain = openssl_decrypt($ct, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv); + if ($plain === false) throw new Exception('openssl_decrypt failed'); + return $plain; +} diff --git a/db_download.php b/db_download.php new file mode 100644 index 0000000..d105e6d --- /dev/null +++ b/db_download.php @@ -0,0 +1,27 @@ +) ---- +$auth = $_SERVER['HTTP_AUTHORIZATION'] ?? ($_SERVER['Authorization'] ?? ''); +if (stripos($auth, 'Bearer ') !== 0) { + http_response_code(401); echo "Fehlender Bearer-Token"; exit; +} +$token = substr($auth, 7); +if (!token_is_valid($token)) { + http_response_code(403); echo "Ungültiger oder abgelaufener Token"; exit; +} + +// ---- 2) Master-verschlüsselte DB laden & entschlüsseln ---- +$dbPath = __DIR__ . '/uploads/questionnaire_database'; +if (!file_exists($dbPath)) { http_response_code(404); echo "Datenbank nicht gefunden"; exit; } +$enc = file_get_contents($dbPath); +if ($enc === false) { http_response_code(500); echo "Lesefehler"; exit; } + +try { + $plain = aes256_cbc_decrypt_bytes($enc, get_master_key_bytes()); +} catch (Throwable $e) { + http_response_code(500); echo "Entschlüsselung fehlgeschlagen"; exit; +} + +// ---- 3) In temporäre SQLite-Datei schreiben ---- +$tmp = tempnam(sys_get_temp_dir(), 'qdb_'); +file_put_contents($tmp, $plain); + +// ---- 4) Tabellen lesen & als HTML ausgeben ---- +try { + $pdo = new PDO("sqlite:$tmp"); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + // Tabellenliste + $tables = []; + $rs = $pdo->query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name"); + foreach ($rs as $row) $tables[] = $row['name']; + + // Ausgabe + echo ""; + echo "

Tabellen: " . htmlspecialchars(implode(', ', $tables)) . "

"; + + foreach ($tables as $t) { + echo "

" . htmlspecialchars($t) . "

"; + + // Spaltenüberschriften via PRAGMA + $cols = []; + $cr = $pdo->query("PRAGMA table_info(" . $pdo->quote($t) . ")"); + // PRAGMA table_info akzeptiert kein quote(..) direkt, also besser plain escapen: + // Workaround: + $t_esc = preg_replace('/[^A-Za-z0-9_]/', '', $t); + $cr = $pdo->query("PRAGMA table_info($t_esc)"); + foreach ($cr as $c) $cols[] = $c['name']; + + // Daten holen (ohne Limit; bei sehr großen Tabellen ggf. LIMIT einbauen) + $stmt = $pdo->query("SELECT * FROM $t_esc"); + echo ""; + foreach ($cols as $c) echo ""; + echo ""; + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + echo ""; + foreach ($cols as $c) { + $val = $row[$c]; + if ($val === null) { echo ""; } + else { echo ""; } + } + echo ""; + } + echo "
" . htmlspecialchars($c) . "
NULL" . htmlspecialchars((string)$val) . "
"; + } +} catch (Throwable $e) { + http_response_code(500); echo "SQLite-Fehler: " . htmlspecialchars($e->getMessage()); +} finally { + @unlink($tmp); +} diff --git a/db_view_ordered.php b/db_view_ordered.php new file mode 100644 index 0000000..b1982cc --- /dev/null +++ b/db_view_ordered.php @@ -0,0 +1,132 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + + $clients = $pdo->query("SELECT clientCode FROM clients ORDER BY clientCode")->fetchAll(PDO::FETCH_COLUMN) ?: []; + $questionnaireIds = $pdo->query("SELECT id FROM questionnaires")->fetchAll(PDO::FETCH_COLUMN) ?: []; + $qidSet = array_fill_keys($questionnaireIds, true); + + $completed = []; + $stmt = $pdo->query("SELECT clientCode, questionnaireId, isDone FROM completed_questionnaires"); + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $c = $row['clientCode'] ?? 'undefined'; + $q = $row['questionnaireId'] ?? 'undefined'; + $completed[$c][$q] = ((int)($row['isDone'] ?? 0)) ? true : false; + } + + $answers = []; + $stmt = $pdo->query("SELECT clientCode, questionId, answerValue FROM answers"); + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + $c = $row['clientCode'] ?? 'undefined'; + $q = $row['questionId'] ?? 'undefined'; + $answers[$c][$q] = (string)($row['answerValue'] ?? ''); + } + + // ---- HTML ---- + echo ''; + + echo ''; + echo ''; + // Row 1: checkbox + IDs (bleiben als technische Schlüssel im Kopf – Werte darunter sind übersetzt) + echo ''; + foreach ($order as $id) echo ''; + echo ''; + + // Row 2: blank + English labels + echo ''; + foreach ($order as $id) { + $lbl = $labels[$id] ?? ''; + echo ''; + } + echo ''; + + foreach ($clients as $client) { + echo ''; + echo ''; + foreach ($order as $id) { + if ($id === 'client_code') { + $val = $client; + } elseif (isset($qidSet[$id]) && strpos($id, '-') === false) { + $val = ($completed[$client][$id] ?? false) ? 'Done' : 'Not Done'; + } else { + $raw = $answers[$client][$id] ?? ''; + $val = (strlen(trim($raw)) > 0) ? t_val($id, $raw, $vals) : 'None'; + } + echo ''; + } + echo ''; + } + echo '
'.htmlspecialchars($id).'
'.htmlspecialchars($lbl).'
'.htmlspecialchars($val).'
'; + +} catch (Throwable $e) { + http_response_code(500); + echo "Error: ".htmlspecialchars($e->getMessage()); +} finally { + @unlink($tmp); +} diff --git a/downloadFull.php b/downloadFull.php new file mode 100755 index 0000000..be12d0e --- /dev/null +++ b/downloadFull.php @@ -0,0 +1,38 @@ + + + + + Questionnaire Database + + + + + +

Questionnaire Database

+ +
+

Login

+
+

+

+ +
+

+
+ + + + + + diff --git a/login.php b/login.php new file mode 100644 index 0000000..0d4df97 --- /dev/null +++ b/login.php @@ -0,0 +1,91 @@ + 'pw1', + 'user02' => 'pw2', + 'Tmp_Daniel' => 'dummy4', + 'Tmp_Johanna' => 'dummy1', + 'Tmp_Anke' => 'dummy2', + 'Tmp_Liliana' => 'dummy3', + 'Amy_tmp' => 'dummy4', + 'Brigitte_tmp' => 'dummy5', + 'Extra1_tmp' => 'dummy6', + 'Extra2_tmp' => 'dummy7', + 'Danny' => 'pw1', + 'User1' => 'pw1', + 'User2' => 'pw2', + 'User3' => 'pw3', + 'User4' => 'pw4', + 'User5' => 'pw5', + 'User6' => 'pw6', + 'User7' => 'pw7', + 'User8' => 'pw8', + 'User9' => 'pw9', + 'User10' => 'pw10', + 'User11' => 'pw11' +]; + +$STORE_FILE = __DIR__ . '/users.json'; // persistenter Speicher für gehashte Passwörter + +function load_store($path) { + if (!file_exists($path)) return []; + $txt = file_get_contents($path); + $arr = json_decode($txt, true); + return is_array($arr) ? $arr : []; +} +function save_store($path, $data) { + file_put_contents($path, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); +} + +$raw = file_get_contents("php://input"); +$data = json_decode($raw, true) ?: []; +$username = trim($data['username'] ?? ''); +$password = (string)($data['password'] ?? ''); + +if ($username === '' || $password === '') { + http_response_code(400); + echo json_encode(["success" => false, "message" => "Username/Passwort fehlt"]); + exit; +} + +// 1) Prüfe, ob Nutzer bereits ein *eigenes*, gehashtes Passwort hat +$store = load_store($STORE_FILE); // Struktur: ["user01" => ["hash" => "...", "changedAt" => 1234567890]] +if (array_key_exists($username, $store)) { + $hash = (string)($store[$username]['hash'] ?? ''); + if ($hash !== '' && password_verify($password, $hash)) { + // ok -> normaler Login + $token = bin2hex(random_bytes(32)); + token_add($token, 30 * 24 * 60 * 60); + echo json_encode(["success" => true, "token" => $token, "user" => $username]); + exit; + } + // hat schon Hash, aber PW falsch + http_response_code(401); + echo json_encode(["success" => false, "message" => "Ungültige Zugangsdaten"]); + exit; +} + +// 2) Nutzer hat *noch* keinen Hash -> nur Default-Zugang erlaubt +if (!array_key_exists($username, $USERS_DEFAULT)) { + http_response_code(401); + echo json_encode(["success" => false, "message" => "Ungültige Zugangsdaten"]); + exit; +} + +if ($USERS_DEFAULT[$username] !== $password) { + http_response_code(401); + echo json_encode(["success" => false, "message" => "Ungültige Zugangsdaten"]); + exit; +} + +// 3) Default-Passwort korrekt -> Passwortwechsel erzwingen +echo json_encode([ + "success" => true, + "user" => $username, + "must_change_password" => true, + "message" => "Bitte Passwort ändern." +]); diff --git a/questions_en.json b/questions_en.json new file mode 100644 index 0000000..09b9c75 --- /dev/null +++ b/questions_en.json @@ -0,0 +1,104 @@ +{ + "client_code": "Client code", + "questionnaire_1_demographic_information": "Questionnaire status", + "questionnaire_1_demographic_information-coach_code": "Coach Code", + "questionnaire_1_demographic_information-consent_instruction": "Obtain client consent (separate document)", + "questionnaire_1_demographic_information-no_consent_entered": "You have indicated that the client has not signed the consent form.", + "questionnaire_1_demographic_information-accommodation": "Accommodation", + "questionnaire_1_demographic_information-other_accommodation": "Other accommodation (name)", + "questionnaire_1_demographic_information-client_code_entry_question": "Please enter client code and coach code.", + "questionnaire_1_demographic_information-age": "Age: How old are you?", + "questionnaire_1_demographic_information-gender": "Gender", + "questionnaire_1_demographic_information-country_of_origin": "Country of origin: where are you from?", + "questionnaire_1_demographic_information-departure_country": "When did you leave your country of origin?", + "questionnaire_1_demographic_information-since_in_germany": "Since when have you been in Germany?", + "questionnaire_1_demographic_information-living_situation": "How are you living here?", + "questionnaire_1_demographic_information-number_family_members": "How many family members are you living with?", + "questionnaire_1_demographic_information-languages_spoken": "Which languages do you speak?", + "questionnaire_1_demographic_information-german_skills": "How would you rate your German language skills?", + "questionnaire_1_demographic_information-school_years_total": "How many years did you attend school?", + "questionnaire_1_demographic_information-school_years_origin": "How many years did you attend school? (in your home country)", + "questionnaire_1_demographic_information-school_years_transit": "How many years did you attend school? (while in transit)", + "questionnaire_1_demographic_information-school_years_germany": "How many years did you attend school? (in Germany)", + "questionnaire_1_demographic_information-vocational_training": "Have you completed vocational training?", + "questionnaire_1_demographic_information-provisional_accommodation_since": "Since when have you been in provisional accommodation?", + "questionnaire_2_rhs": "Questionnaire status", + "questionnaire_2_rhs-coach_code": "Coach Code", + "questionnaire_2_rhs-glass_explanation": "The response options are shown in words and as a picture of a glass container: if something does not apply at all, the glass is empty; if it applies somewhat or fairly, the glass fills up; if it applies extremely, the glass is full.", + "questionnaire_2_rhs-q1_symptom": "1. Muscle, bone, or joint pain", + "questionnaire_2_rhs-q2_symptom": "2. Feeling unhappy, sad, or depressed most of the time", + "questionnaire_2_rhs-q3_symptom": "3. Overthinking or worrying too much", + "questionnaire_2_rhs-q4_symptom": "4. Feeling helpless", + "questionnaire_2_rhs-q5_symptom": "5. Being easily startled for no reason", + "questionnaire_2_rhs-q6_symptom": "6. Fatigue, dizziness, or feeling weak", + "questionnaire_2_rhs-q7_symptom": "7. Feeling nervous or insecure", + "questionnaire_2_rhs-q8_symptom": "8. Feeling restless, unable to sit still", + "questionnaire_2_rhs-q9_symptom": "9. Feeling like crying easily", + "questionnaire_2_rhs-q10_reexperience_trauma": "10. ... re-experiencing the trauma; behaving or feeling as if it were happening again?", + "questionnaire_2_rhs-q11_physical_reaction": "11. ... experiencing physical reactions (e.g., sweating, rapid heartbeat) when reminded of the trauma?", + "questionnaire_2_rhs-q12_emotional_numbness": "12. ... feeling emotionally numb (e.g., feeling sad but unable to cry, unable to feel loving emotions)?", + "questionnaire_2_rhs-q13_easily_startled": "13. ... being more easily startled (e.g., when someone approaches from behind)?", + "questionnaire_2_rhs-q14_intro": "14. Thinking about your life in the past four weeks, to what extent do you feel that you:", + "questionnaire_2_rhs-pain_rating_instruction": "Choose the number (0–10) that best describes how much suffering the respondent experienced in the past week, including today.", + "questionnaire_2_rhs-violence_question_1": "Have you ever been injured by others so seriously that you needed medical attention? (doctor, hospital)", + "questionnaire_2_rhs-times_happend": "Has this ... happened?", + "questionnaire_2_rhs-age_at_incident": "At what age: at ... years?", + "questionnaire_2_rhs-conflict_since_arrival": "Since arriving in Germany, have you ever been involved in violent conflicts (physical fights, physical or psychological conflicts)?", + "questionnaire_2_rhs-times_happend2": "Has this ... happened?", + "questionnaire_2_rhs-age_at_incident2": "At what age: at ... years?", + "questionnaire_2_rhs-asylum_procedure_since": "Since when has your asylum procedure been ongoing?", + "questionnaire_3_integration_index": "Questionnaire status", + "questionnaire_3_integration_index-coach_code": "Coach Code", + "questionnaire_3_integration_index-feeling_connected_to_germany_question": "How connected do you feel to Germany (how well have you settled in Germany)?", + "questionnaire_3_integration_index-understanding_political_issues": "How well do you understand the most important political issues in Germany?", + "questionnaire_3_integration_index-unexpected_expense_question": "Imagine you had to make an unexpected but necessary payment. Which of the following amounts could you pay or afford (if you had free access to money)?", + "questionnaire_3_integration_index-dining_with_germans_question": "How often have you dined (e.g. dinner) with Germans in the past 12 months?", + "questionnaire_3_integration_index-reading_german_articles_question": "How well can you read simple newspaper articles in German about familiar topics (health, home, family, etc.) and understand the main points?", + "questionnaire_3_integration_index-visiting_doctor_question": "How easy or difficult would it be for you to see a doctor in this country? (making an appointment, etc. - not: 'you can't get an appointment', 'I would never go to a doctor here')", + "questionnaire_3_integration_index-feeling_as_outsider_question": "How often do you feel like an outsider in Germany?", + "questionnaire_3_integration_index-recent_occupation_question": "How have you been occupied in the last four weeks? (Please select only one answer)", + "questionnaire_3_integration_index-discussing_politics_question": "Since arriving in Germany, how often have you discussed important political topics in Germany with other people?", + "questionnaire_3_integration_index-contact_with_germans_question": "With how many Germans have you exchanged messages (phone, messenger chat (WhatsApp, Facebook...), or SMS) in the last 4 weeks?", + "questionnaire_3_integration_index-speaking_german_opinion_question": "How well can you participate in a conversation in German on familiar topics (children, family, school, home, etc.) and express your opinion?", + "questionnaire_3_integration_index-job_search_question": "How easy or difficult would it be for you to search for and find a job in this country (even if you may not be allowed to work: what would be your assumption if there were no restrictions)?", + "questionnaire_4_consultation_results": "Questionnaire status", + "questionnaire_4_consultation_results-coach_code": "Coach Code", + "questionnaire_4_consultation_results-date_consultation_health_interview_result": "Date of counseling interview (health interview result green/yellow/red)", + "questionnaire_4_consultation_results-consultation_decision": "Counseling decision (0)", + "questionnaire_4_consultation_results-consent_conversation_in_6_months": "Consent for conversation in 6 months:", + "questionnaire_4_consultation_results-participation_in_coaching": "Participation In Coaching", + "questionnaire_4_consultation_results-consent_coaching_given": "Consent for coaching is given", + "questionnaire_4_consultation_results-decision_after_reflection_period": "Decision on .............. (date) after reflection period", + "questionnaire_4_consultation_results-professional_referral": "Professional Referral", + "questionnaire_4_consultation_results-confidentiality_agreement": "Confidentiality Agreement", + "questionnaire_4_consultation_results-health_insurance_card": "Health Insurance Card", + "questionnaire_5_final_interview": "Final Interview", + "questionnaire_5_final_interview-coach_code": "Coach Code", + "questionnaire_5_final_interview-consent_followup_6_months": "Consent for conversation in 6 months", + "questionnaire_5_final_interview-date_final_interview": "Date of final interview", + "questionnaire_5_final_interview-amount_nat_appointments": "Number of NAT appointments (after the counseling interview)", + "questionnaire_5_final_interview-amount_session_flowers": "Number of storytelling sessions with flowers", + "questionnaire_5_final_interview-amount_session_stones": "Number of storytelling sessions with stones", + "questionnaire_5_final_interview-termination_nat_coaching": "Termination of NAT coaching", + "questionnaire_5_final_interview-client_canceled_NAT": "Client discontinued NAT", + "questionnaire_6_follow_up_survey": "Questionnaire status", + "questionnaire_6_follow_up_survey-coach_code": "Coach Code", + "questionnaire_6_follow_up_survey-follow_up": "Follow-up survey", + "questionnaire_6_follow_up_survey-special_burden_question": "Was there any special burden?", + "questionnaire_6_follow_up_survey-glass_explanation": "The response options are shown in words and as a picture of a glass container: if something does not apply at all, the glass is empty; if it applies somewhat or fairly, the glass fills up; if it applies extremely, the glass is full.", + "questionnaire_6_follow_up_survey-how_strong_past_month": "How strongly did you experience the following in the past month...", + "questionnaire_6_follow_up_survey-q14_intro": "14. Thinking about your life in the past four weeks, to what extent do you feel that you:", + "questionnaire_6_follow_up_survey-pain_rating_instruction": "Choose the number (0–10) that best describes how much suffering the respondent experienced in the past week, including today.", + "questionnaire_6_follow_up_survey-feeling_connected_to_germany_question": "How connected do you feel to Germany (how well have you settled in Germany)?", + "questionnaire_6_follow_up_survey-understanding_political_issues": "How well do you understand the most important political issues in Germany?", + "questionnaire_6_follow_up_survey-unexpected_expense_question": "Imagine you had to make an unexpected but necessary payment. Which of the following amounts could you pay or afford (if you had free access to money)?", + "questionnaire_6_follow_up_survey-dining_with_germans_question": "How often have you dined (e.g. dinner) with Germans in the past 12 months?", + "questionnaire_6_follow_up_survey-reading_german_articles_question": "How well can you read simple newspaper articles in German about familiar topics (health, home, family, etc.) and understand the main points?", + "questionnaire_6_follow_up_survey-visiting_doctor_question": "How easy or difficult would it be for you to see a doctor in this country? (making an appointment, etc. - not: 'you can't get an appointment', 'I would never go to a doctor here')", + "questionnaire_6_follow_up_survey-feeling_as_outsider_question": "How often do you feel like an outsider in Germany?", + "questionnaire_6_follow_up_survey-recent_occupation_question": "How have you been occupied in the last four weeks? (Please select only one answer)", + "questionnaire_6_follow_up_survey-discussing_politics_question": "Since arriving in Germany, how often have you discussed important political topics in Germany with other people?", + "questionnaire_6_follow_up_survey-contact_with_germans_question": "With how many Germans have you exchanged messages (phone, messenger chat (WhatsApp, Facebook...), or SMS) in the last 4 weeks?", + "questionnaire_6_follow_up_survey-speaking_german_opinion_question": "How well can you participate in a conversation in German on familiar topics (children, family, school, home, etc.) and express your opinion?", + "questionnaire_6_follow_up_survey-job_search_question": "How easy or difficult would it be for you to search for and find a job in this country (even if you may not be allowed to work: what would be your assumption if there were no restrictions)?" +} diff --git a/tokens.jsonl b/tokens.jsonl new file mode 100644 index 0000000..7bf436c --- /dev/null +++ b/tokens.jsonl @@ -0,0 +1,192 @@ +{"token":"731c101342f056ba57da7f958ba683fb41b0381d42de69090bcabee8c1b3a389","created":1756809382,"exp":1756895782} +{"token":"a1ba2845684ab849d364a84027670c3c6357028b3d1afb5f5b4868e904805235","created":1756809404,"exp":1756895804} +{"token":"6d2a7a1686cddb29441dfbe2e261d853ec55a1d46e1bbb8db3e4f8d0ce953937","created":1756809519,"exp":1756895919} +{"token":"1a0bc695ff5854e16b76dc60d5cbf51529942430b2d5315a36e0266136b9ab43","created":1756809545,"exp":1756895945} +{"token":"375ace02859b27ca837bfa5a130e6237b7c7b39720ff5d880b3519e2c8bbff57","created":1756809577,"exp":1756895977} +{"token":"95c8f8cd5a300c2357fb237c55932dd15079d777c69ac9c523f347c98e6a196c","created":1756809590,"exp":1756895990} +{"token":"66048cfaf8ba428c18125328d3d92ee5ef85fb5da4b57ca10e30ed49e65cb7a3","created":1756990520,"exp":1757076920} +{"token":"74b33d1ec4314def382db73b25a68bb835b847399587b9e1c753b5fc6d7ac526","created":1756990626,"exp":1757077026} +{"token":"cf64baa593827d9d213f65da58f68e4be4fede9d1d089f667247985244218b79","created":1757055382,"exp":1757141782} +{"token":"91161cab2fad81cc2fbd5bec8990bc763169b9151bd5686d5643a7df119ddbeb","created":1757057047,"exp":1757143447} +{"token":"01390c7efae09a04917042c7a33ee669f1f399aea1ad6e38fdf0ea128cb56293","created":1757057152,"exp":1757143552} +{"token":"76da7ea81a475703f54e628d34349cf58825f5c69a83cd7ac28864d46a070f3e","created":1757057205,"exp":1757143605} +{"token":"f39965dc4374d6e6a4ff1d7f3cfcebf8a46591496778de606d5e8be6583fc32b","created":1757057967,"exp":1757144367} +{"token":"91d056c9e6c74d6cfb3a7dfc490e3fb74e0fa1107a644fa9ebc770046ad10108","created":1757058317,"exp":1757144717} +{"token":"a50ca166c451e4eb1a6ecfbd8e8947ab33adc41638cfe490844d4f216df1b2b7","created":1757058371,"exp":1757144771} +{"token":"4fa698c244a21802e1001cd9e2ca95e3836f8a40b87130ba0bbf56e4409a5258","created":1757058387,"exp":1757144787} +{"token":"a0c28eaaf54cc6c85b0aa28abaf3579f7c730127f84782223b549eb69a4fbda2","created":1757315000,"exp":1757401400} +{"token":"e03f93c020e7a1e63ca7bd35e8996effad1f164e771747248086e6e1062361a9","created":1757318539,"exp":1757404939} +{"token":"a2b06b8b6d4c4f13c0e1764b444c9d54b6391a44df11d76e07b388f010cef535","created":1757318594,"exp":1757404994} +{"token":"df8b9dc5ac814565c4fc7543d7dca8385103de68c3463c4a6bf751ec935a930e","created":1757319230,"exp":1757405630} +{"token":"ea183dbe572c64451825d0383554ec325494f79a342de8be082ca356bd634861","created":1757326842,"exp":1757413242} +{"token":"f221237385a68cae4487815580ffde5dba191ab26e530629fbb9d25cfbd78b9b","created":1757328356,"exp":1757414756} +{"token":"1f1fbe3e4e61c93bf0dda1ecd1be15613d33a2065458789465ae6390df55bb6a","created":1758107596,"exp":1758193996} +{"token":"9b36e1cd9064f7a0d0b6932d7e2540c8167b5f0217d25ecb3366890cbe45ad0b","created":1758107713,"exp":1758194113} +{"token":"c5a33030ac64f9695c8afde31dd5c11846e9047a2dea117f49a133f90ce76924","created":1758107738,"exp":1758194138} +{"token":"f2077115a4969f4f17d14b1245d93afee249b9dc224472ca9fb3626a88c767df","created":1758477721,"exp":1758564121} +{"token":"fb6e30161a17fcf4d26fff6b6fdf9390c516cda64dcb3df9d7785f22ff18b1d1","created":1758477745,"exp":1758564145} +{"token":"767d37875a3d3e26eed32a0856ab36e81b500155a2a86b0349b185ec7fb3a090","created":1758477764,"exp":1758564164} +{"token":"606140c6c1c72ee6ed237b9bcd1d06f8e0706d693be2a04eeae4e90830a5bd18","created":1758477789,"exp":1758564189} +{"token":"5dca3d7add783b6448d863809bcc4389578c4b8512fe3f6f24c7cdbef4e1b908","created":1758523992,"exp":1758610392} +{"token":"ecb9246a30d8c24398351fe69b40730d5fa3b1ccc237f822cc6ec568702bdc61","created":1758524058,"exp":1758610458} +{"token":"971f7fc5b21463b1180ce20152e8e9412e6582638f02d0d3f0ab5b87e831c92c","created":1758524093,"exp":1758610493} +{"token":"02ec7c0520bb2e9992012a276fac309770aead2d1e21e201142c88e85dd77938","created":1758524109,"exp":1758610509} +{"token":"b6844c5a99952786846d065777bb33647e24dd59371c6e16b37aa6fde8d08635","created":1758524126,"exp":1758610526} +{"token":"e07239755729bbd43f13112d25a3fd7ca652de6705e24fa49bfe9fbe67a94a43","created":1758524433,"exp":1758610833} +{"token":"0e0e946dbef1255217b9418adf577d79d195db8e2e1060932cc824ba6efaed11","created":1758524480,"exp":1758610880} +{"token":"6d2a941987d0753b8f58d3058039ebc091286030be9f0d6be08a666c6212f325","created":1758631441,"exp":1758717841} +{"token":"a2cc4bedf2d3a070546a795cd2799e0e6ba7a5640c31acc458909d7467fbacfd","created":1758631465,"exp":1758717865} +{"token":"5e5d72eb15330fc4dbfb3acf262d43c730307b04206234f22ddf7efa784857f4","created":1758631622,"exp":1758718022} +{"token":"83e5311d921f2d77a13b3bffc3173e3f1ab402de5a0983da046dac3735d50669","created":1758631641,"exp":1758718041} +{"token":"51835ba874245fb5fb31bf795ab418173a1ac842c50d0555464c61b452d0759b","created":1758631699,"exp":1758718099} +{"token":"f7d1a8f2625a4dc23caf983f78ca6e6cc09fb5a1c9e622ffc47d194bcb1eb2fc","created":1758631715,"exp":1758718115} +{"token":"f262efa480d0d98034e6139072f672a8c287454a415327472b8d8b76fc431bb5","created":1758631821,"exp":1758718221} +{"token":"b3fff94205e321bca2bfa80e5edfafa51a42e1e94f2f5813dc1c226d98e9a21f","created":1758631831,"exp":1758718231} +{"token":"10ba334c91ab6af3f8cb768cd6e139b3fecaf180a7c4a44ac8d6f25712e4a5a9","created":1758632063,"exp":1758718463} +{"token":"53b7527c88487d67a77e110b5274c72d5620088aae58debfdd68fd1b9583438a","created":1758632075,"exp":1758718475} +{"token":"a8e92c5cfcce9f4e4a3402a6b275230d759dd2f0c4de0577d237e5bdebdd7a39","created":1758632214,"exp":1758718614} +{"token":"93c49902b428c4073dd61e93fbd3b3267dc99710e0eace885362c1d36d1bff11","created":1758632231,"exp":1758718631} +{"token":"9bbbfbb833576b33140add4a3a251d5d5d4d130b82c95aa37e8d5f67b6b3fa47","created":1758634360,"exp":1758720760} +{"token":"3f6d20603b463bbff6369b75ed20c482c088d194604438af636a065cc00d38cc","created":1758634417,"exp":1758720817} +{"token":"16f4c983c3b186d282b5b68534e1d581e6aa2922c9d8fcecda9c4de9481d85ee","created":1758634669,"exp":1758721069} +{"token":"ac0e1352a82161ec19577823712b93f62c6eecc645248f8213d80ee5d056aef8","created":1758634694,"exp":1758721094} +{"token":"c44e10696b9de668527ee7e645cb44c7aba983a5890b71dd3fc923ebe93807b2","created":1758634748,"exp":1758721148} +{"token":"7547fd0a6593b28c4f4778ba93a00fa93e905b4d99aef38d4cd3215254659d1e","created":1758634779,"exp":1758721179} +{"token":"03c005ba089d5ca9db37dec87abcc87eda453a11c88cc9e0129c9c4420c66cd7","created":1758634837,"exp":1758721237} +{"token":"17a40e3d0752d7b40b1ecdff2fef13f1a670f027d2152eaf60146ce988cf8418","created":1758634850,"exp":1758721250} +{"token":"404a6ce228679470fd88b9926777238bbc4c3487dda478f5e9ba68cff9be24ba","created":1758637923,"exp":1758724323} +{"token":"9306963145c2beac257849cd41d8a8994899c04eb1661bb891b765d3eac4d202","created":1758638114,"exp":1758724514} +{"token":"02f79fd4f0d88863f6277552aa73a58f44d3706e2bc916f9bfa155b51047c124","created":1758638334,"exp":1758724734} +{"token":"d7722fecb0fcc0f4788fdbfd7c9d72dca05ee9417992426c3a0289dd605ea4cd","created":1758638870,"exp":1758725270} +{"token":"e8f527f42d8a27af0a8dfc1ab94811c9654327542ee6fbf56ed762677d06d24e","created":1758638916,"exp":1758725316} +{"token":"b8b66d92eba410abf7cde0d47528dca87526d0e6ebff12ec6d872ff56a5dfbda","created":1758638951,"exp":1758725351} +{"token":"f7c5cf139714a7d161fef9ff8de76a68131b1bebfc44ccbe2540fe9bd078f0ca","created":1758713063,"exp":1758799463} +{"token":"7b969f437896d654492db1f012bd15454f7d9a54547372a00d500e8575c18ebc","created":1758880572,"exp":1758966972} +{"token":"217f9fc39604e187023233900ea85c3cd6b0500b9a2e31f96b61b26b2b03757e","created":1758880587,"exp":1758966987} +{"token":"54fc17d45268b6653d8354d52a0a538eb62d1dd602e874aed5527f3d70ae9bc4","created":1758880618,"exp":1758967018} +{"token":"2f3f7fd350188548cbbd76959a91120bf67c871d8b0ce0d372a7394a6c958b49","created":1758880849,"exp":1758967249} +{"token":"76758ca57e42ed78504d148e243d9c333a83431b0072f59807e3ba906576c714","created":1758881595,"exp":1758967995} +{"token":"7543060413d38da66b7b0b80a444e8e37d3317c0be32585682365007ff6ce176","created":1758881763,"exp":1758968163} +{"token":"8ceff5d37ee6e6bd58b349fce81fd2d7814280799897e1f3ec94dc2c02b5044b","created":1758882150,"exp":1758968550} +{"token":"f813faa3aced8e2bb4ee7f815a0e5cd7c15cd8a314d178a9bd4adaa1d65ad7a8","created":1758963705,"exp":1759050105} +{"token":"30172769015f9d95aca3cff58dcc8e82dd1c34ff34e5fa421b93f852c2efc995","created":1758963970,"exp":1759050370} +{"token":"286f6448269afb14ad3579b2ec20edf5a5215efc7b8c61a4da42a81f670eea5a","created":1758964604,"exp":1759051004} +{"token":"602ba60c2b42590fd6b81fa0374769c3fa5419e6431ad936556c53f858e0dd3d","created":1758964964,"exp":1759051364} +{"token":"60d72e4dd79c595caa31a363a18fb65d53b408de657be558428486e0f95c1624","created":1759130219,"exp":1759216619} +{"token":"7ecf40a37b1860287ab9749039dd06b1aa41fc968daee96abb86b1f5640e9a3b","created":1759131060,"exp":1759217460} +{"token":"9f7caf0c8b690935664c8458297becfe0f01d4c636cc27739b6a4bb422dce023","created":1759131973,"exp":1759218373} +{"token":"49c9c4105eaae364566eb87e8ade6f76ee501fe304bc34a435ce5f59ba6d0d61","created":1759132236,"exp":1759218636} +{"token":"0bf4c2a9b0635fd0a9e9e6729f958c0c521ba03763740ffe35423d006382f3d2","created":1759132261,"exp":1759218661} +{"token":"4a227bacb48a669ed6f767cd5355bbb2b7ebdb8413439cd880debf8be6f05854","created":1759132276,"exp":1759218676} +{"token":"9f835e179b993f1ce748f3a8deffb5e2b6c420ba0ed256d76b1008bccb09592b","created":1759132305,"exp":1759218705} +{"token":"97726da2f384f90cc9772113d25b05f270d83bd95d8e7174217e0f1b90b6c0d8","created":1759132645,"exp":1759219045} +{"token":"12602dd163016ff3f4cc44e2024af559ab1d3ee4da03b1dd80b9c89bde3b7dac","created":1759132832,"exp":1759219232} +{"token":"555ce8248ee91024d3d78a29a001411643739d68bbd1b845331423cb03a2b686","created":1759132975,"exp":1759219375} +{"token":"23920ccf38cd1d89b8600be7634adf5b568dd65c55683d1aa9ae0065f07d9dd0","created":1759133185,"exp":1759219585} +{"token":"05223b7302ff2ce1920395b840dbaf901b03a19e74e586c89e557c7ecd5c045a","created":1759133317,"exp":1759219717} +{"token":"4bb6e11294e7c1231420ed75a9bb6cdd65c29b1af3a83e33663d41cb64b0db2e","created":1759133349,"exp":1759219749} +{"token":"c23f83b74435194855203c703a4c763b69fe3de6161e64528855887551edd777","created":1759133401,"exp":1759219801} +{"token":"48ca9bc7937f8ec478f97b8d77680a507f671ce02bae4c354ac66a924b4d9a63","created":1759133422,"exp":1759219822} +{"token":"e6952e1cb227a985cd8546ec8008f7d606ab329cf1d19bb148957a2bb794fa8f","created":1759133445,"exp":1759219845} +{"token":"fe1dfd9aa8bdc9717fd2dfbd3867771c979b5e49fab8f45342b8405e5dddfb8d","created":1759133762,"exp":1759220162} +{"token":"d62b8043df3bbdbdb787806c5b492ceaa505402d099ba38c78a46a409d0c33f0","created":1759133809,"exp":1759220209} +{"token":"6c78c2e05d9021ca8a050954104c9e0c31f76e944fdac5d56c7227dbba048f72","created":1759133839,"exp":1759220239} +{"token":"13ccd9f6051c1a11e6950fed92cd0c4e60cf595e47a07cbb25f283ceac7bbf43","created":1759133903,"exp":1759220303} +{"token":"0d9e3dc9e5aef1fcf579e6dc55bb989d84af7e27999295cb60900206a2b043e7","created":1759134295,"exp":1759220695} +{"token":"5248a0c841ae09db2166f1cd3254b1672031f321c71212e775b07794ee2efb99","created":1759136855,"exp":1759223255} +{"token":"dd9941e9b616fe179e33c0a884a3bfc3ada56c2f626afcb9da63e3e585ab9226","created":1759137413,"exp":1759223813} +{"token":"ecced21404122b68765272c3a1c5707be9ae230ab0cf193d661fc4b09bafe78d","created":1759137429,"exp":1759223829} +{"token":"8e013fbd30ee8278356f08e0aa0d3f6e4e709ae5c5246a731a8034070eda7079","created":1759137677,"exp":1759224077} +{"token":"b35841e30ea56d0bbf1274a7606294d1b85d149aebca4389675cb47731a25e1d","created":1759138841,"exp":1759225241} +{"token":"a82c1f1b051f51b33f1f3e676511a07f4914395c1fddce1aeee41aa0c398fa27","created":1759138863,"exp":1759225263} +{"token":"6647b9b2900320da05769955bdc172dabab24c6a24d258659593978d7b8d8be9","created":1759138920,"exp":1759225320} +{"token":"68b87e342ee484d6b9d278913ed8ca7864780e2672c520941eca7adcdabc90d0","created":1759138985,"exp":1759225385} +{"token":"41584f24f999a2b08bca7a26716b7bb82e27df6f3bf22efd925a05c1e49e7f4e","created":1759138997,"exp":1759225397} +{"token":"ee10e4e87b7c80c875e622d53fc1f3658b4d7e6d4cfb86a8b1af8a00537d76af","created":1759139042,"exp":1759225442} +{"token":"4a9fc9ad92631aa019e2f4a6c829bc47277bfad191ef7a0cff744b30949e5002","created":1759139881,"exp":1759226281} +{"token":"a3888918ee647f213e7408e6fff916e99f6fb3e757fe44a02f279b508da2016f","created":1759139945,"exp":1759226345} +{"token":"9b574f805ec4ab45a99aadf98246cc3cac3839cc25cc9f1f40ffc4a314cf5390","created":1759141376,"exp":1759227776} +{"token":"ef0d5340827ffc436c88863199d820cf7d8143f260c4f3f89a9438cbbae9e9ea","created":1759141871,"exp":1759228271} +{"token":"2d51a5ead5266958a15313b8dd92109d90ae0069c476328af7e764e3284e6563","created":1759141889,"exp":1759228289} +{"token":"01ce0e40ecd1253e53eaba374b09e363535fe511cea5c11a9cc7e2d50b5a5d70","created":1759141930,"exp":1759228330} +{"token":"d2a967c7ef1cfd6444f9f98865b71b7a50eee6fb8746b33f753bec23fe8e134c","created":1759141955,"exp":1759228355} +{"token":"31b442f6c1787dfc4090e8687d697bf3b9947acee107595054014892688b69fc","created":1759143187,"exp":1759229587} +{"token":"2cb95a73cd824886d5a8ef8f78e4ea50bec48545399b4ec8a97569e11ad1fae7","created":1759143242,"exp":1759229642} +{"token":"1270a387b5ed061801df25c587a32c16d19e01705bad9b33b5c8d081a8a6e405","created":1759665689,"exp":1759752089} +{"token":"cf99f7b82cf9d5929e171c4a10bfe2f804f8dbcfa8d4615032d116975273a20a","created":1759670447,"exp":1759756847} +{"token":"99a2398703836d3b4ad35122f1eceaaf752404a61df5932684f1bde2983065b0","created":1759671255,"exp":1759757655} +{"token":"492feb4f9c7cbdc9ff81003cf0ea821584ac9018d556ce64303e39f402644151","created":1759672995,"exp":1759759395} +{"token":"4950d4b18494285205bd5a1925d89c74a9a27d8bf61fea546a60c3ff77434c8a","created":1759769234,"exp":1759855634} +{"token":"d15b65be6c96d98d273bf75542da813d8bdf9a3e67b21b5d6e6ffc9578d825f6","created":1759769252,"exp":1759855652} +{"token":"7455244c86cfa13f6a35bb350669fb118ed559eec1b04615af6e61df24b15860","created":1759770329,"exp":1759856729} +{"token":"de71102f6b394bf1c9fefdabb3f81063947971d9c0b3039044f8a5ccb06fe0b9","created":1760019733,"exp":1760106133} +{"token":"33dfc2f32a92c33add844c063b6441435fbd81b099687f562f91bb844cce51fd","created":1760019746,"exp":1760106146} +{"token":"af9b3962b69dd2ed8101a6c82a4d2b3c16e6dcfe9bd2f704dad5ad89e5f4ec78","created":1760020288,"exp":1760106688} +{"token":"60476805d087f95b73bfc85906f3da2d564f0da823be997bff56840b9a3cbf83","created":1760020419,"exp":1760106819} +{"token":"e2865b9b9e93ab3a6824bbfc7b8a538d9504c5e742d8311ac8cff1cd5b1be537","created":1760020537,"exp":1760106937} +{"token":"e9525d496c03eab391ff7a33bebcf894e27739dbe72dbfd78561bd3f0abeeb04","created":1760020598,"exp":1760106998} +{"token":"3dab956d4162eafcafbc909774e5c72b8f705bd0118526ec64fc9aad779ffbd1","created":1760100224,"exp":1760186624} +{"token":"46048543edd7e1164f09e15ad7b270d0cbe902a8e9cf83c74b336126e9812919","created":1760100259,"exp":1760186659} +{"token":"c6769ca917c5b7bc3a8de449d4c1ee432a48af2816106d45151da78b6df5f6fe","created":1760102088,"exp":1760188488} +{"token":"1d661c5092d3f5cb49d9040eca309d899d76aab6853f730e10e11251aeb0e3e6","created":1760102101,"exp":1760188501} +{"token":"18678f9d4b37859266ce5f94974b50b8b79c6b2f8ba95b1127e21e55584242a1","created":1760102109,"exp":1760188509} +{"token":"4d0242faef13d10b3a601a221193e541eafaa4bc3ee6bd803c1ffb7a50839809","created":1760102145,"exp":1760188545} +{"token":"7b70d5aa0f414c658968462359345b2a3a14a9d4a5975b6e11120b474cda67ea","created":1760102318,"exp":1760188718} +{"token":"5cc22039581e43ee6ca998bddef9f38406074365ebab0f19f78a3770d129c80a","created":1760102851,"exp":1760189251} +{"token":"ca738bb4509efb56d9ed890202fe610e086e6c060a937fe24f0950d5e1ad0e91","created":1760102867,"exp":1760189267} +{"token":"63a52bf36dfdbdf183bc3645c0499c7b9d7407ff4e0b68d0ba4a7c4d21b3b2bd","created":1760373199,"exp":1760459599} +{"token":"540025ab075bd0b0d5b5f32136a2d728d33940428f1e03cb1cbefa2a4031cb22","created":1760373303,"exp":1760459703} +{"token":"698c35a7a528eff612691bd1e5b8d87606282e602946f4f3e780ad07882ccf39","created":1760376043,"exp":1760462443} +{"token":"03014ddfae73da00708075e584b7acc7453bd6fb09336ea4d0f311dea048cf74","created":1760423624,"exp":1760510024} +{"token":"59fe7551c92538ba16a8cb037cb4841e804955e541a711b93ce872c0cbfa4822","created":1760428837,"exp":1760515237} +{"token":"4e721a7c891ce14f50525807fefa7f30935fb646abcbe797a57967638d3153b1","created":1760429061,"exp":1760515461} +{"token":"df53f6ee07a698402ae87f2b61d0f1449eb9e8947379f8b5e49416577f44466e","created":1760429937,"exp":1760516337} +{"token":"72d697bda7b5b1b6c433f77d4974d056101cbfe59ed4a3b797b43e501e7315ae","created":1760602832,"exp":1760689232} +{"token":"f0f8bf32768a2741cc503b990d18123c5d4baf492e9a8d842eaaa1010ee5bb88","created":1760608588,"exp":1760694988} +{"token":"b7840ea72c124152e09992cc55bb8ea4239ec90fb5970caa2142344be49cd4c5","created":1760608677,"exp":1760695077} +{"token":"08aa5a790cc9fd8cef3b9a75b5b65286544c008dc2a9ea8f0d43b9e25fbe19a7","created":1760610683,"exp":1760697083} +{"token":"2cbc45e37c995d2673eb5b5d4f5dbc470e69b30b634972bf358c92afe841da06","created":1760610695,"exp":1760697095} +{"token":"bc6cf1156147f85d1b20982dd9f8730bf0fbf50b5740f62cf52bea5d5dd28c82","created":1760611508,"exp":1760697908} +{"token":"f1ff89caf2b5b3a68df5b091a9f158db2174f8e865f7d632cabe156c96847099","created":1760612940,"exp":1760699340} +{"token":"5cd4cc46d876fb7f6ac0435dd35f7b1f10637ef6aea0084cddcf701ee385747d","created":1760612996,"exp":1760699396} +{"token":"7d88c0fa36dc13981eacf5b57f36d666727cc516ef546a84e5708b0c47998175","created":1760613447,"exp":1760699847} +{"token":"627765059218d2e0f829887025a3c2c3319dbbfa052698ef4bbfa9932da34cd7","created":1760971357,"exp":1761057757} +{"token":"be417f82627bee9786ea5f8b99ce64a4dc855114f3e90e81192243bd7f9ee53c","created":1760972135,"exp":1761058535} +{"token":"4c4a6345cdd1a3a5556e44d2d306224eaf00ae580c46c4e0a305f9bf83ae1f1b","created":1761039024,"exp":1761125424} +{"token":"7b8def5d4ba5727c1332601b9652440c4850cfaa87cd93b38b5575b6ee2b93cc","created":1761064340,"exp":1761150740} +{"token":"33b38bbae91754d7459ea880a76f348765bc8c9a348ea32217efc40b487bcceb","created":1763290822,"exp":1763377222} +{"token":"9dde0839b583c875468530ee6fa2a92b85f4689bee30f59dd4e321fc2893cccd","created":1771865346,"exp":1771951746} +{"token":"cd7ef79a5664faf211e7508aa3e1da38a9295d3d7f30852abf87f657c4545ef3","created":1771865413,"exp":1771951813} +{"token":"3f71f2fde72c9fa670e4e17ac67da022952e5526fe3b4ffd70820046bab7dc7d","created":1771865429,"exp":1771951829} +{"token":"4eed29d1c55d2bcfc9fb218177062cd55f502ebca01915f121c4f725ab3886f3","created":1771865524,"exp":1771951924} +{"token":"508fdc8268cdbf8e58acbc6d0a2113f4f266f562ec2e7bbd2b4586226930bc1c","created":1771865748,"exp":1771952148} +{"token":"ea7c5c53ae3add1263435239a377f55a8edb3c3db0bb9d6ca8ca9755f40c952c","created":1771866052,"exp":1771952452} +{"token":"16db690623237f5f1e748c335ff72beb34be1ddf8117937705fc3f956d5854d4","created":1771866144,"exp":1771952544} +{"token":"7c079d53022f27743a8f41c2c16ba241622dde305d07df3da142feb5ce109a7a","created":1771866278,"exp":1771952678} +{"token":"afd3d79a917c910596bffd65ddd3d806f2c8c99f0ab9e398080989e0c352a3a0","created":1771866586,"exp":1771952986} +{"token":"e320bc456c9ab043f6259d687ff8074f0cd10ba63618f89671d584243b7be9e1","created":1771866962,"exp":1771953362} +{"token":"b37345318d18290039246d9473d0dcd64794ee2e9c024ecf6ec2c80a2d81820a","created":1771937505,"exp":1772023905} +{"token":"275398f85a005c7e58bdac6c5b001e544aee3cec9f5d323c4f98fe8578f39ae7","created":1771937569,"exp":1772023969} +{"token":"0476b9a7134403ab3fb28e0da8b00897a18bd1f20c58b3f66e49427f4e4fab17","created":1771939864,"exp":1772026264} +{"token":"6e4372fb37e0abd54d5a559978b1742bfbead965376df39439c3b809857dead9","created":1771940580,"exp":1772026980} +{"token":"dc58bb4fde7a94f169998ce31a1ac52433572b52f696d8af298dfe4191b3864c","created":1771940628,"exp":1772027028} +{"token":"e11133b3ab5c850ecc75a6496a882a7e7b348d4c449eba7d6192f598defdb229","created":1771940848,"exp":1772027248} +{"token":"44e8ac2a418d7b759c061d7f6187a0023736d2a0104b7ea70c1459ec8dab4700","created":1771941296,"exp":1772027696} +{"token":"9d39a349397f05b07b11fb1b07fe04d703030ed322d5f61bdf9b3dd81e244b59","created":1772000804,"exp":1772087204} +{"token":"b3e448c188ec718cf7aec6601fbd800cb087d08475ee95adc753eaab1a6ea800","created":1772001151,"exp":1772087551} +{"token":"9714b678caa1dded7222bb00ac466c8163bf1003dbaaf290d694be764e4a5c46","created":1772001535,"exp":1772087935} +{"token":"48721aaa13f78d571a28275e39fec81586eabda937fee765720ec72e42d82aa1","created":1772002283,"exp":1772088683} +{"token":"36242c20ed90459c72d12d7541311d803da304e3a50848a06ee47b0896543e9c","created":1772002329,"exp":1772088729} +{"token":"af278b2e4b05d94ff422dc4391e4aa1a5c09d43f3b34f1e3e522696ab631e07a","created":1772101322,"exp":1772187722} +{"token":"7abe6b6ffb9f168bcce197ee0e2287d9ee5055551d73aea70e479a8e4ef6ef01","created":1772106567,"exp":1772192967} +{"token":"88052fc72bf9ad9f99b70c4197322aa270dfef966e50a2ad268f62cbbae9e8d9","created":1772110997,"exp":1772197397} +{"token":"af85e2994c1d9e844d9179171e57ff3542de845967f1d1f4f90107029d814f20","created":1772111983,"exp":1772198383} +{"token":"3119ce71143809f78316ce131bc2e93bbf113b7b2a103fa20d584f233efc7a43","created":1772112290,"exp":1772198690} +{"token":"0474561405352eb731e66ed121a827c951673b7ff1b7032ba6195f9d19abde59","created":1772114237,"exp":1772200637} +{"token":"12719b1ce7992358d537eacb1d39397292acbc76fa12bde72c9d4314ae6f80dd","created":1772114779,"exp":1772201179} +{"token":"173d5f63ae00a092ebd135f0c09efe959bd23e426fb13159f8dea14f1dc4b924","created":1772283082,"exp":1772369482} +{"token":"8527a42eedef65315ed02f22f0a489279b5f8fe9f90a3a3e657fe4db3fdc5f48","created":1772452494,"exp":1772538894} +{"token":"8b9a0f52b37fa7dd28fc52ea8f6b6d40d60240dd978a1b78d1c295594836b954","created":1772698644,"exp":1772785044} +{"token":"d0bd7435c514c3f090d06ab92bc208b5d8eabd7cddd333427bd329257521a124","created":1772698738,"exp":1772785138} +{"token":"8f0959185b3c22aa86310192e8e81096c503305e211780a0e9a446711e762639","created":1772699719,"exp":1775291719} +{"token":"9bd42a9a3d9364f3087dd6e15b991daf764b4510cec728369e5b870f08ca91ad","created":1772703629,"exp":1775295629} diff --git a/translations_en.json b/translations_en.json new file mode 100644 index 0000000..57c862b --- /dev/null +++ b/translations_en.json @@ -0,0 +1,348 @@ +{ + "client_code_entry_question": "Please enter client code and coach code.", + "all_fields_between_0_and_15": "Please enter a number between 0 and 15 in all fields!", + "fill_both_fields": "Please fill in both fields!", + "please_answer_all": "Please answer all questions!", + "select_accommodation": "Please select an accommodation!", + "enter_coach_code": "Please enter your coach code to continue!", + "enter_country": "Please enter a country to continue!", + "only_letters_allowed": "Please enter letters only!", + "enter_valid_number": "Please enter a valid number!", + "enter_valid_year": "Please enter a valid year!", + "enter_text_to_continue": "Please enter text to continue!", + "select_at_least_one_language": "Please select at least one language!", + "select_country": "Please select a country", + "select_gender": "Please select a gender!", + "select_one_answer": "Please select one answer!", + "select_one_option": "Please select one option!", + "coach_code": "Coach Code", + "client_code": "Client Code", + "select_month": "Select month!", + "select_year": "Select year!", + "choose_answer": "Choose answer", + "choose_option": "Choose option!", + "consent_not_signed": "Consent not signed", + "consent_signed": "Consent signed", + "error_file": "Error processing file!", + "no_school_attended": "I did not attend school!", + "once": "once", + "year_after_2000": "The year must be after 2000!", + "year_after_departure": "The year must be after leaving the country of origin!", + "year_max": "The year must be less than or equal to $MAX_VALUE_YEAR!", + "data_final_warning": "Important: The data cannot be changed or edited after completion!", + "multiple_times": "multiple times", + "more_than_15_years": "more than 15 years", + "no": "No", + "no_answer": "No answer", + "other_country": "Other country", + "value_must_be_less_equal_max": "The value must be less than or equal to $MAX_VALUE_AGE!", + "value_between_1_and_15": "The value must be between 1 and 15!", + "invalid_month": "Invalid month!", + "invalid_year": "Invalid year!", + "yes": "Yes", + "next": "Next", + "previous": "Back", + "finish": "Complete data entry", + "thank_you_participation": "Thank you for participating in this survey.", + "response_recorded": "Your response has been recorded.", + "january": "January", + "february": "February", + "march": "March", + "april": "April", + "may": "May", + "june": "June", + "july": "July", + "august": "August", + "september": "September", + "october": "October", + "november": "November", + "december": "December", + "consent_instruction": "Obtain client consent (separate document)", + "no_consent_entered": "You have indicated that the client has not signed the consent form.", + "no_consent_note": "This is important information for the evaluation of 'BW schützt!'.", + "coach_code_request": "Please enter your coach code and the name of the accommodation below. Then upload the data as usual.", + "other_accommodation": "Other accommodation (name)", + "age": "Age: How old are you?", + "gender": "Gender", + "gender_male": "Male", + "gender_female": "Female", + "gender_diverse": "Diverse", + "country_of_origin": "Country of origin: where are you from?", + "country_text_entry": "Country of origin (text entry)", + "departure_country": "When did you leave your country of origin?", + "year": "Year", + "since_in_germany": "Since when have you been in Germany?", + "living_situation": "How are you living here?", + "alone": "Alone", + "with_family": "With family members", + "number_family_members": "How many family members are you living with?", + "number_label": "Number of family members", + "languages_spoken": "Which languages do you speak?", + "language_albanian": "Albanian", + "language_pashto": "Pashto", + "language_russian": "Russian", + "language_serbo": "Serbo-Croatian", + "language_somali": "Somali", + "language_tamil": "Tamil", + "language_tigrinya": "Tigrinya", + "language_turkish": "Turkish", + "language_ukrainian": "Ukrainian", + "language_urdu": "Urdu", + "language_other": "Other", + "language_arabic": "Arabic", + "language_dari_farsi": "Dari/Farsi", + "language_chinese": "Chinese", + "language_english": "English", + "language_macedonian": "Macedonian", + "language_kurmanji": "Kurdish-Kurmanji", + "language_hindi": "Hindi", + "language_french": "French", + "german_skills": "How would you rate your German language skills?", + "skill_none": "None", + "skill_basic": "Basic knowledge", + "skill_a1": "A1", + "skill_a2": "A2", + "skill_b1": "B1", + "skill_b2": "B2", + "skill_c1": "C1", + "skill_c2": "C2", + "school_years_origin": "How many years did you attend school? (in your home country)", + "school_years_transit": "How many years did you attend school? (while in transit)", + "school_years_germany": "How many years did you attend school? (in Germany)", + "label_years": "Years", + "vocational_training": "Have you completed vocational training?", + "answer_no": "No", + "answer_started": "Started", + "answer_completed": "Completed", + "provisional_accommodation_since": "Since when have you been in provisional accommodation?", + "asylum_procedure_since": "Since when has your asylum procedure been ongoing?", + "accommodation": "Accommodation", + "steinstrasse": "Steinstraße", + "doerfle": "Dörfle", + "zoll_emmishofer": "Zoll (Emmishofer Straße)", + "other": "Other", + "school_years_total": "How many years did you attend school?", + "glass_explanation": "The response options are shown in words and as a picture of a glass container: if something does not apply at all, the glass is empty; if it applies somewhat or fairly, the glass fills up; if it applies extremely, the glass is full.", + "symptoms": "Symptoms", + "never_glass": "Never", + "little_glass": "A little", + "moderate_glass": "Moderately", + "much_glass": "Much", + "extreme_glass": "Extremely", + "q1_symptom": "1. Muscle, bone, or joint pain", + "q2_symptom": "2. Feeling unhappy, sad, or depressed most of the time", + "q3_symptom": "3. Overthinking or worrying too much", + "q4_symptom": "4. Feeling helpless", + "q5_symptom": "5. Being easily startled for no reason", + "q6_symptom": "6. Fatigue, dizziness, or feeling weak", + "q7_symptom": "7. Feeling nervous or insecure", + "q8_symptom": "8. Feeling restless, unable to sit still", + "q9_symptom": "9. Feeling like crying easily", + "how_strong_past_month": "How strongly did you experience the following in the past month...", + "q10_reexperience_trauma": "10. ... re-experiencing the trauma; behaving or feeling as if it were happening again?", + "q11_physical_reaction": "11. ... experiencing physical reactions (e.g., sweating, rapid heartbeat) when reminded of the trauma?", + "q12_emotional_numbness": "12. ... feeling emotionally numb (e.g., feeling sad but unable to cry, unable to feel loving emotions)?", + "q13_easily_startled": "13. ... being more easily startled (e.g., when someone approaches from behind)?", + "instruction_listen_statements": "I will now read 5 statements to you. Please listen carefully and then tell me which one best applies to you:", + "q14_intro": "14. Thinking about your life in the past four weeks, to what extent do you feel that you:", + "q14_option_1": "Are able to handle everything that comes your way", + "q14_option_2": "Are able to handle most things that come your way", + "q14_option_3": "Are able to handle some things but not others", + "q14_option_4": "Are unable to handle most things", + "q14_option_5": "Are unable to handle anything", + "pain_rating_instruction": "Choose the number (0–10) that best describes how much suffering the respondent experienced in the past week, including today.", + "violence_intro": "Finally, I have 2 questions about experiences of violence:", + "violence_question_1": "Have you ever been injured by others so seriously that you needed medical attention? (doctor, hospital)", + "times_happend": "Has this ... happened?", + "age_at_incident": "At what age: at ... years?", + "times_happend2": "Has this ... happened?", + "age_at_incident2": "At what age: at ... years?", + "conflict_since_arrival": "Since arriving in Germany, have you ever been involved in violent conflicts (physical fights, physical or psychological conflicts)?", + "finish_data_entry": "When you have finished entering the data, click \"Save\".", + "feeling_connected_to_germany_question": "How connected do you feel to Germany (how well have you settled in Germany)?", + "intro_life_in_germany": "Now we would like to ask you some questions about your life in Germany:", + "not_connected_at_all": "not connected at all", + "somewhat_loose": "somewhat loose", + "moderately_connected": "moderately connected", + "quite_connected": "quite connected", + "very_connected": "very connected", + "understanding_political_issues": "How well do you understand the most important political issues in Germany?", + "very_good": "very good", + "good": "good", + "fairly_good": "fairly good", + "somewhat": "somewhat", + "not_at_all": "not at all", + "unexpected_expense_question": "Imagine you had to make an unexpected but necessary payment. Which of the following amounts could you pay or afford (if you had free access to money)?", + "expense_50": "up to €50", + "expense_100": "€100", + "expense_300": "€300", + "expense_500": "€500", + "expense_800": "€800", + "dining_with_germans_question": "How often have you dined (e.g. dinner) with Germans in the past 12 months?", + "never": "never", + "once_a_year": "once a year", + "once_a_month": "once a month", + "once_a_week": "once a week", + "almost_every_day": "almost every day", + "reading_german_articles_question": "How well can you read simple newspaper articles in German about familiar topics (health, home, family, etc.) and understand the main points?", + "not_good": "not well", + "visiting_doctor_question": "How easy or difficult would it be for you to see a doctor in this country? (making an appointment, etc. - not: 'you can't get an appointment', 'I would never go to a doctor here')", + "very_difficult": "very difficult", + "rather_difficult": "rather difficult", + "neither_nor": "neither difficult nor easy", + "rather_easy": "rather easy", + "very_easy": "very easy", + "feeling_as_outsider_question": "How often do you feel like an outsider in Germany?", + "rarely": "rarely", + "sometimes": "sometimes", + "often": "often", + "always": "always", + "recent_occupation_question": "How have you been occupied in the last four weeks? (Please select only one answer)", + "employed": "in paid employment, even if only temporarily (employed, self-employed, minor jobs)", + "education": "in education (school, vocational training, university)", + "internship": "internship", + "unemployed_searching": "unemployed and actively looking for work", + "unemployed_not_searching": "unemployed and not actively looking for work", + "sick_or_disabled": "ill or unable to work", + "unpaid_housework": "in unpaid housework, childcare / caring for others", + "other_activity": "Other (e.g., language course)", + "discussing_politics_question": "Since arriving in Germany, how often have you discussed important political topics in Germany with other people?", + "contact_with_germans_question": "With how many Germans have you exchanged messages (phone, messenger chat (WhatsApp, Facebook...), or SMS) in the last 4 weeks?", + "zero": "0", + "one_to_three": "1 to 3", + "three_to_six": "3 to 6", + "seven_to_fourteen": "7 to 14", + "fifteen_or_more": "15 or more", + "speaking_german_opinion_question": "How well can you participate in a conversation in German on familiar topics (children, family, school, home, etc.) and express your opinion?", + "moderately_good": "moderately good", + "job_search_question": "How easy or difficult would it be for you to search for and find a job in this country (even if you may not be allowed to work: what would be your assumption if there were no restrictions)?", + "integration_index": "Integration Index: %d", + "consent_followup_6_months": "Consent for conversation in 6 months", + "day": "Day", + "month": "Month", + "select_day": "Please select a day.", + "select_month": "Please select a month.", + "select_year": "Please select a year.", + "date_final_interview": "Date of final interview", + "amount_nat_appointments": "Number of NAT appointments (after the counseling interview)", + "amount_session_flowers": "Number of storytelling sessions with flowers", + "amount_session_stones": "Number of storytelling sessions with stones", + "termination_nat_coaching": "Termination of NAT coaching", + "termination_nat_regular": "Regular (mutual decision to terminate after discussing all important events)", + "termination_nat_referral": "Referral to a specialized professional (doctor, psychologist, ...)", + "termination_nat_dropout": "Client discontinued NAT", + "follow_up": "Follow-up survey", + "follow_up_completed": "was successfully conducted", + "follow_up_failed_contact": "could not be conducted: repeated failed contact attempts", + "follow_up_consent_withdrawn": "could not be conducted: consent withdrawn", + "special_burden_question": "Was there any special burden?", + "resilience_fully_capable": "Able to handle everything that comes your way", + "resilience_mostly_capable": "Able to handle most things that come your way", + "resilience_partially_capable": "Able to handle some things but not others", + "resilience_mostly_not_capable": "Unable to handle most things", + "resilience_not_capable": "Unable to handle anything", + "intro_resilience_questions": "I will now read 5 statements to you. Please listen carefully and then tell me which statement best applies to you:", + "resilience_reflection_prompt": "Thinking about your life in the past four weeks, to what extent do you feel that you:", + "resilience_some_capable_some_not": "Able to handle most things but unable to handle some others", + "follow_up_survey_score": "The RHS total score is: %d", + "select_one_answer_per_row": "Please select one answer per row!", + "no_next_question_defined": "No forwarding page defined", + "date_consultation_health_interview_result": "Date of counseling interview (health interview result green/yellow/red)", + "consultation_decision": "Counseling decision (RHS_POINTS)", + "consent_conversation_in_6_months": "Consent for conversation in 6 months:", + "participation_in_coaching": "Participation in coaching", + "decision_after_reflection_period": "Decision on .............. (date) after reflection period", + "participation_coaching": "Coaching participation", + "consent_coaching_given": "Consent for coaching is given", + "consent_form_must_be_available": "Consent form must be available!", + "professional_referral": "Professional referral", + "confidentiality_agreement": "Confidentiality agreement", + "health_insurance_card": "Health insurance card", + "green": "green", + "yellow": "yellow", + "red": "red", + "time_to_think_about_it": "Reflection period", + "consent_yes": "yes, consent is given", + "consent_no": "no, consent is not given", + "available_yes": "yes, available", + "available_no": "no, not available", + "client_canceled_NAT": "Client discontinued NAT", + "after_meeting": "after session", + "without_giving_reasons": "without giving reasons", + "with_reasons_given": "with reasons given", + "select_at_least_one_answer": "Please select at least one answer.", + "select_at_least_minimum": "Please select at least %d answers.", + "demographic_information": "Demographic information", + "rhs": "RHS", + "integration_index_label": "Integration index", + "consultation_results": "Counseling results", + "final_interview": "Final interview", + "follow_up_survey": "Follow-up survey", + "example_text": "Example text", + "client_code_exists": "This client code already exists.", + "load": "Load", + "date_after": "The date must be after", + "date_before": "The date must be before", + "lay": "it.", + "choose_more_elements": "More elements must be selected.", + "please_client_code": "Please enter client code", + "no_profile": "This client is not yet part of the database", + "questionnaires_finished": "All questionnaires have been completed for this client!", + "edit": "Edit", + "save": "Save", + "upload": "Upload", + "download": "Download", + "database": "Database", + "clients": "Clients", + "client": "Client", + "client_code_label": "Client Code", + "questionnaires": "Questionnaires", + "questionnaire": "Questionnaire", + "questionnaire_id": "Questionnaire ID", + "status": "Status", + "header_label": "Header", + "id": "ID", + "value": "Value", + "no_clients": "No clients available.", + "no_questionnaires": "No questionnaires available.", + "no_questions": "No questions available.", + "question": "Question", + "answer": "Answer", + "download_header": "Download header", + "back": "Back", + "export_success_downloads_headers": "Export successful: Downloads/ClientHeaders.xlsx", + "export_failed": "Export failed.", + "error_generic": "Error", + "not_done": "Not Done", + "none": "None", + "done": "Done", + "locked": "Locked", + "start": "Start", + "points": "Points", + "saved_pdf_csv": "PDF and CSV were saved in the \"Downloads\" folder.", + "no_pdf_viewer": "No PDF viewer installed.", + "save_error": "Error while saving: {message}", + "login_required_title": "Login required", + "username_hint": "Username", + "password_hint": "Password", + "login_btn": "Login", + "exit_btn": "Exit", + "please_username_password": "Please enter username and password.", + "download_failed_no_local_db": "Download failed – no local database available", + "download_failed_use_offline": "Download failed – working offline with existing database", + "login_failed_with_reason": "Login failed: {reason}", + "no_header_template_found": "No header template found", + "login_required": "Please log in first", + "session_label": "Session", + "session_dash": "Session: —", + "hours_short": "h", + "minutes_short": "min", + "online": "Online", + "offline": "Offline", + "open_client_via_load": "Please open the client via \"Load\".", + "database_clients_title": "Database – Clients", + "no_clients_available": "No clients available.", + "headers": "Headers", + "no_questions_available": "No questions available.", + "view_missing": "Missing view: %s" +} diff --git a/uploadDelta.log b/uploadDelta.log new file mode 100755 index 0000000..0ea89c2 Binary files /dev/null and b/uploadDelta.log differ diff --git a/uploadDeltaTest5.php b/uploadDeltaTest5.php new file mode 100644 index 0000000..b3db9d2 --- /dev/null +++ b/uploadDeltaTest5.php @@ -0,0 +1,466 @@ += $now; + } + } + fclose($h); + } + } + $txt = __DIR__ . '/valid_tokens.txt'; + if (file_exists($txt)) { + $arr = file($txt, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if ($arr && in_array($token, $arr, true)) return true; + } + return false; +} + +// ----------------------------- +// Hauptlogik +// ----------------------------- +try { + if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + http_response_code(405); + echo json_encode(["error" => "Nur POST erlaubt"]); + exit; + } + + // Token aus multipart oder Authorization: Bearer + $token = $_POST['token'] ?? null; + if (!$token) { + $auth = $_SERVER['HTTP_AUTHORIZATION'] ?? ($_SERVER['Authorization'] ?? ''); + if (stripos($auth, 'Bearer ') === 0) { + $token = substr($auth, 7); + } + } + if (!$token || !token_is_valid($token)) { + http_response_code(403); + echo json_encode(["error" => "Ungültiger Token"]); + exit; + } + + // Upload entgegennehmen (multipart 'file' oder raw body) + $uploadedFilePath = null; + if (!empty($_FILES['file']) && is_uploaded_file($_FILES['file']['tmp_name'])) { + $uploadedFilePath = $_FILES['file']['tmp_name']; + } else { + $raw = file_get_contents('php://input'); + if ($raw !== false && strlen($raw) > 0) { + $tmp = tempnam(sys_get_temp_dir(), 'upl_'); + file_put_contents($tmp, $raw); + $uploadedFilePath = $tmp; + } + } + if (!$uploadedFilePath) { + http_response_code(400); + echo json_encode(["error" => "Keine Datei gesendet"]); + exit; + } + + // Payload mit SESSION-Key (aus Token via HKDF) entschlüsseln + $encData = file_get_contents($uploadedFilePath); + if ($encData === false) { + http_response_code(500); + echo json_encode(["error" => "Fehler beim Lesen der hochgeladenen Datei"]); + exit; + } + $sessionKey = hkdf_session_key_from_token($token); + $jsonData = null; + try { + $jsonData = aes256_cbc_decrypt_bytes($encData, $sessionKey); + } catch (Throwable $e) { + // Fallback: akzeptiere Plain-JSON (Kompatibilität) + $maybeJson = trim($encData); + if ($maybeJson === '' || json_decode($maybeJson, true) === null) { + log_msg("Entschlüsselung fehlgeschlagen und kein valides JSON. raw len=" . strlen($encData)); + http_response_code(400); + echo json_encode(["error" => "Ungültige verschlüsselte Datei oder kein JSON"]); + exit; + } + $jsonData = $maybeJson; + } + + $data = json_decode($jsonData, true); + if (!is_array($data)) { + http_response_code(400); + echo json_encode(["error" => "Ungültiges JSON"]); + exit; + } + + // Ziel: MASTER-verschlüsselte DB speichern + $dbPath = __DIR__ . '/uploads/questionnaire_database'; + if (!is_dir(dirname($dbPath))) { + if (!mkdir(dirname($dbPath), 0755, true) && !is_dir(dirname($dbPath))) { + throw new Exception("Konnte Upload-Ordner nicht erstellen"); + } + } + + // Lock, damit keine parallelen Writes kollidieren + $lockFile = __DIR__ . '/uploads/.qdb_lock'; + $lockFp = fopen($lockFile, 'c'); + if ($lockFp === false) throw new Exception("Konnte Lock-Datei nicht öffnen"); + if (!flock($lockFp, LOCK_EX)) { fclose($lockFp); throw new Exception("Konnte Lock nicht setzen"); } + + $tmpDb = null; + $tmpEncrypted = null; + + try { + $tmpDb = tempnam(sys_get_temp_dir(), 'qdb_'); + $masterKey = get_master_key_bytes(); + + // Bestehende MASTER-verschlüsselte DB entschlüsseln oder neue DB mit Schema anlegen + if (file_exists($dbPath) && is_file($dbPath)) { + $storedEnc = @file_get_contents($dbPath); + if ($storedEnc === false) throw new Exception("Konnte gespeicherte DB nicht lesen"); + $decrypted = aes256_cbc_decrypt_bytes($storedEnc, $masterKey); + if (file_put_contents($tmpDb, $decrypted) === false) throw new Exception("Konnte temporäre DB nicht schreiben"); + } else { + $pdo = new PDO("sqlite:$tmpDb"); + $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $pdo->exec("PRAGMA foreign_keys = ON;"); + $pdo->exec(" + CREATE TABLE IF NOT EXISTS clients ( + clientCode TEXT NOT NULL DEFAULT 'undefined' PRIMARY KEY + ); + CREATE TABLE IF NOT EXISTS questionnaires ( + id TEXT NOT NULL DEFAULT 'undefined' PRIMARY KEY + ); + CREATE TABLE IF NOT EXISTS questions ( + questionId TEXT NOT NULL DEFAULT 'undefined' PRIMARY KEY, + questionnaireId TEXT NOT NULL, + question TEXT NOT NULL DEFAULT '', + FOREIGN KEY(questionnaireId) REFERENCES questionnaires(id) ON DELETE CASCADE + ); + CREATE TABLE IF NOT EXISTS answers ( + clientCode TEXT NOT NULL, + questionId TEXT NOT NULL, + answerValue TEXT NOT NULL DEFAULT '', + PRIMARY KEY (clientCode, questionId), + FOREIGN KEY(clientCode) REFERENCES clients(clientCode) ON DELETE CASCADE, + FOREIGN KEY(questionId) REFERENCES questions(questionId) ON DELETE CASCADE + ); + CREATE TABLE IF NOT EXISTS completed_questionnaires ( + clientCode TEXT NOT NULL, + questionnaireId TEXT NOT NULL, + timestamp INTEGER NOT NULL, + isDone INTEGER NOT NULL, + sumPoints INTEGER, + PRIMARY KEY (clientCode, questionnaireId), + FOREIGN KEY(clientCode) REFERENCES clients(clientCode) ON DELETE CASCADE, + FOREIGN KEY(questionnaireId) REFERENCES questionnaires(id) ON DELETE CASCADE + ); + "); + $pdo = null; + } + + // Einspielen der Daten + $db = new PDO("sqlite:$tmpDb"); + $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $db->exec("PRAGMA foreign_keys = ON;"); + $db->beginTransaction(); + + // --- Sammeln, welche IDs/Paarungen betroffen sind --- + $questionnaireIds = []; // set[string] questionnaireId => true + $clientQuestionPairs = []; // set["client|qid"] => true + + if (!empty($data['questionnaires'])) { + foreach ($data['questionnaires'] as $q) { + if (isset($q['id']) && $q['id'] !== null && $q['id'] !== '') { + $questionnaireIds[$q['id']] = true; + } + } + } + if (!empty($data['questions'])) { + foreach ($data['questions'] as $q) { + if (isset($q['questionnaireId']) && $q['questionnaireId'] !== null && $q['questionnaireId'] !== '') { + $questionnaireIds[$q['questionnaireId']] = true; + } + } + } + if (!empty($data['completed_questionnaires'])) { + foreach ($data['completed_questionnaires'] as $c) { + if (isset($c['questionnaireId']) && $c['questionnaireId'] !== null && $c['questionnaireId'] !== '') { + $questionnaireIds[$c['questionnaireId']] = true; + } + if (isset($c['clientCode']) && isset($c['questionnaireId'])) { + $client = ($c['clientCode'] === null || $c['clientCode'] === '') ? 'undefined' : $c['clientCode']; + $qid = ($c['questionnaireId'] === null || $c['questionnaireId'] === '') ? 'undefined' : $c['questionnaireId']; + $clientQuestionPairs["$client|$qid"] = true; + } + } + } + if (!empty($data['answers'])) { + foreach ($data['answers'] as $a) { + if (isset($a['clientCode'])) { + $client = ($a['clientCode'] === null || $a['clientCode'] === '') ? 'undefined' : $a['clientCode']; + $qid = null; + if (isset($a['questionId']) && is_string($a['questionId'])) { + $parts = explode('-', $a['questionId'], 2); + if (count($parts) >= 1) { + $potential = $parts[0]; + if ($potential !== '') $qid = $potential; + } + } + if ($qid !== null) { + $questionnaireIds[$qid] = true; + $clientQuestionPairs["$client|$qid"] = true; + } + } + } + } + if (!empty($data['clients']) && !empty($data['questionnaires'])) { + foreach ($data['clients'] as $c) { + if (!isset($c['clientCode'])) continue; + $client = ($c['clientCode'] === null || $c['clientCode'] === '') ? 'undefined' : $c['clientCode']; + foreach ($data['questionnaires'] as $q) { + if (!isset($q['id'])) continue; + $qid = ($q['id'] === null || $q['id'] === '') ? 'undefined' : $q['id']; + $clientQuestionPairs["$client|$qid"] = true; + } + } + } + + // --- Löschungen (zuerst abhängige Tabellen) --- + $delAnswersByPairStmt = $db->prepare(" + DELETE FROM answers + WHERE clientCode = :clientCode + AND questionId IN (SELECT questionId FROM questions WHERE questionnaireId = :questionnaireId) + "); + $delCompletedByPairStmt = $db->prepare(" + DELETE FROM completed_questionnaires + WHERE clientCode = :clientCode + AND questionnaireId = :questionnaireId + "); + foreach ($clientQuestionPairs as $pair => $_) { + list($clientCode, $qid) = explode('|', $pair, 2); + try { + $delAnswersByPairStmt->execute([ + ':clientCode' => $clientCode, + ':questionnaireId' => $qid + ]); + $delCompletedByPairStmt->execute([ + ':clientCode' => $clientCode, + ':questionnaireId' => $qid + ]); + log_msg("Deleted answers & completed for client='$clientCode' questionnaire='$qid'"); + } catch (Exception $e) { + log_msg("Delete error for pair $pair: " . $e->getMessage()); + } + } + + if (!empty($questionnaireIds)) { + $delQuestionsStmt = $db->prepare("DELETE FROM questions WHERE questionnaireId = :questionnaireId"); + foreach ($questionnaireIds as $qid => $_) { + try { + $delQuestionsStmt->execute([':questionnaireId' => $qid]); + log_msg("Deleted questions for questionnaireId='$qid'"); + } catch (Exception $e) { + log_msg("Delete questions error for $qid: " . $e->getMessage()); + } + } + } + + // --- Inserts/Upserts --- + if (!empty($data['clients'])) { + $stmt = $db->prepare("INSERT OR IGNORE INTO clients (clientCode) VALUES (:clientCode)"); + foreach ($data['clients'] as $client) { + if (!isset($client['clientCode'])) continue; + $val = $client['clientCode'] === null || $client['clientCode'] === '' ? 'undefined' : $client['clientCode']; + $stmt->execute([':clientCode' => $val]); + } + } + + if (!empty($data['questionnaires'])) { + $stmt = $db->prepare("INSERT OR IGNORE INTO questionnaires (id) VALUES (:id)"); + foreach ($data['questionnaires'] as $q) { + if (!isset($q['id'])) continue; + $val = $q['id'] === null || $q['id'] === '' ? 'undefined' : $q['id']; + $stmt->execute([':id' => $val]); + } + } + + if (!empty($data['questions'])) { + $stmt = $db->prepare(" + INSERT OR IGNORE INTO questions (questionId, questionnaireId, question) + VALUES (:questionId, :questionnaireId, :question) + "); + foreach ($data['questions'] as $q) { + if (!isset($q['questionId'])) continue; + $questionId = $q['questionId'] === null || $q['questionId'] === '' ? 'undefined' : $q['questionId']; + $questionnaireId = $q['questionnaireId'] === null || $q['questionnaireId'] === '' ? 'undefined' : $q['questionnaireId']; + $questionText = $q['question'] ?? ''; + $stmt->execute([ + ':questionId' => $questionId, + ':questionnaireId' => $questionnaireId, + ':question' => $questionText + ]); + } + } + + if (!empty($data['answers'])) { + $stmt = $db->prepare(" + INSERT OR IGNORE INTO answers (clientCode, questionId, answerValue) + VALUES (:clientCode, :questionId, :answerValue) + "); + foreach ($data['answers'] as $a) { + if (!isset($a['clientCode']) || !isset($a['questionId'])) continue; + $clientCode = $a['clientCode'] === null || $a['clientCode'] === '' ? 'undefined' : $a['clientCode']; + $questionId = $a['questionId'] === null || $a['questionId'] === '' ? 'undefined' : $a['questionId']; + $answerValue = array_key_exists('answerValue', $a) && $a['answerValue'] !== null ? $a['answerValue'] : ''; + $stmt->execute([ + ':clientCode' => $clientCode, + ':questionId' => $questionId, + ':answerValue' => $answerValue + ]); + } + } + + if (!empty($data['completed_questionnaires'])) { + $stmt = $db->prepare(" + INSERT OR REPLACE INTO completed_questionnaires (clientCode, questionnaireId, timestamp, isDone, sumPoints) + VALUES (:clientCode, :questionnaireId, :timestamp, :isDone, :sumPoints) + "); + foreach ($data['completed_questionnaires'] as $c) { + if (!isset($c['clientCode']) || !isset($c['questionnaireId'])) continue; + $clientCode = $c['clientCode'] === null || $c['clientCode'] === '' ? 'undefined' : $c['clientCode']; + $questionnaireId = $c['questionnaireId'] === null || $c['questionnaireId'] === '' ? 'undefined' : $c['questionnaireId']; + $timestamp = isset($c['timestamp']) ? (int)$c['timestamp'] : time(); + $isDone = !empty($c['isDone']) ? 1 : 0; + $sumPoints = array_key_exists('sumPoints', $c) ? ($c['sumPoints'] === null ? null : (int)$c['sumPoints']) : null; + $stmt->execute([ + ':clientCode' => $clientCode, + ':questionnaireId' => $questionnaireId, + ':timestamp' => $timestamp, + ':isDone' => $isDone, + ':sumPoints' => $sumPoints + ]); + } + } + + $db->commit(); + $db = null; + + // MASTER-verschlüsselt speichern (atomar via temp + rename) + $plainDb = file_get_contents($tmpDb); + if ($plainDb === false) throw new Exception("Konnte tmp DB nicht lesen"); + $enc = aes256_cbc_encrypt_bytes($plainDb, $masterKey); + + $tmpEncrypted = tempnam(dirname($dbPath), 'enc_qdb_'); + if (file_put_contents($tmpEncrypted, $enc) === false) throw new Exception("Konnte verschlüsselte DB nicht schreiben"); + if (!@rename($tmpEncrypted, $dbPath)) { + if (!@copy($tmpEncrypted, $dbPath) || !@unlink($tmpEncrypted)) { + throw new Exception("Konnte verschlüsselte DB nicht speichern (rename/copy fehlgeschlagen)"); + } + } + @chmod($dbPath, 0644); + + // Cleanup + @unlink($uploadedFilePath); + @unlink($tmpDb); + flock($lockFp, LOCK_UN); + fclose($lockFp); + + echo json_encode(["success" => true, "message" => "Delta erfolgreich eingespielt und DB gespeichert"]); + exit; + + } catch (Throwable $inner) { + // Cleanup bei Fehler + @flock($lockFp, LOCK_UN); + @fclose($lockFp); + @unlink($tmpDb ?? ''); + @unlink($tmpEncrypted ?? ''); + @unlink($uploadedFilePath ?? ''); + log_msg("Inner exception: " . $inner->getMessage()); + http_response_code(500); + echo json_encode(["error" => "Fehler beim Speichern", "message" => $inner->getMessage()]); + exit; + } + +} catch (Throwable $e) { + log_msg("Top-level exception: " . $e->getMessage()); + http_response_code(500); + echo json_encode(["error" => "Fehler", "message" => $e->getMessage()]); + exit; +} diff --git a/uploads/.qdb_lock b/uploads/.qdb_lock new file mode 100644 index 0000000..e69de29 diff --git a/uploads/questionnaire_database b/uploads/questionnaire_database new file mode 100644 index 0000000..1c16aad Binary files /dev/null and b/uploads/questionnaire_database differ diff --git a/users.json b/users.json new file mode 100644 index 0000000..16fa4ff --- /dev/null +++ b/users.json @@ -0,0 +1,50 @@ +{ + "user01": { + "hash": "$2y$10$fcB5mBc.RDeCHyY69RScZOpFL/IjY.wFzx1DNzKI7F6kJhd9rwQfu", + "changedAt": 1760610683 + }, + "user02": { + "hash": "$2y$10$fGDcFLBgL3oypyjkwVna/OhQ5Be6UFH6hNGdupeNfJMASgrFK7h0K", + "changedAt": 1760611508 + }, + "Tmp_Daniel": { + "hash": "$2y$10$NRdP4/vkDQEM4fD70xGqdO1dgujOBATcA00Zj2aIaH0hoQV5Rp8ri", + "changedAt": 1760612940 + }, + "Tmp_Liliana": { + "hash": "$2y$10$4Gui323PXRnQ9Ix4WSrefOEr2HQwfbr6TNVaa7qVE2q59ZwBpnisa", + "changedAt": 1760971357 + }, + "Tmp_Johanna": { + "hash": "$2y$10$B4eNyer1anHYMNjpNliIW.54sK8z1cYyQEHjmHloatUnmeilchjva", + "changedAt": 1760972135 + }, + "Danny": { + "hash": "$2y$10$JreuWqvh1VO3fH2YhSpqveDpSSgVPUeNW91ySSzxeQ0o/OgNe1gGS", + "changedAt": 1771865346 + }, + "Extra1_tmp": { + "hash": "$2y$10$zUtdsv1N2Uv/7XZT4wr4lOTnJEWFF74EJp2/kgq6GHpO8dguMQify", + "changedAt": 1771865748 + }, + "User11": { + "hash": "$2y$10$ql0aI9C.1nzkE33Oxy8T3.dE0jF0y42fe4x8YWyqFm.SwlUBoqv22", + "changedAt": 1772001151 + }, + "User10": { + "hash": "$2y$10$5HoG6T6UA9CFd2q1VGLiE.3EbwNyyVR8kSuTZWxJ5run8KvHgrhhK", + "changedAt": 1772110997 + }, + "User2": { + "hash": "$2y$10$/KxLlL9ZdHnGYL1y4IEIfOnWL.bd2HgPVfk.n5V0Sc.3LzDwmeWl6", + "changedAt": 1772111983 + }, + "User3": { + "hash": "$2y$10$GMtUWbW.bczjc43dpf539OE7rFgwL08QV1qnjFlyDh5mF/Ss4atom", + "changedAt": 1772112290 + }, + "User7": { + "hash": "$2y$10$AfJp8go8Vt5J0.46SqYmtOPrrp1CqkWw1pmkOGI6xZ5MgqHTmwIga", + "changedAt": 1772283082 + } +} \ No newline at end of file diff --git a/valid_tokens.txt b/valid_tokens.txt new file mode 100644 index 0000000..f2d0d40 Binary files /dev/null and b/valid_tokens.txt differ