From 77742275e6857d00a6f6c31c56422eba77e15d21 Mon Sep 17 00:00:00 2001 From: oxidiert Date: Tue, 16 Sep 2025 15:51:11 +0200 Subject: [PATCH] finished header and changed colors. --- app/src/main/assets/header_order.json | 106 ------------------ app/src/main/assets/header_order.xlsx | Bin 0 -> 10212 bytes .../com/dano/test1/DatabaseButtonHandler.kt | 106 +++++++++++++----- 3 files changed, 79 insertions(+), 133 deletions(-) delete mode 100644 app/src/main/assets/header_order.json create mode 100644 app/src/main/assets/header_order.xlsx diff --git a/app/src/main/assets/header_order.json b/app/src/main/assets/header_order.json deleted file mode 100644 index 31c0c8e..0000000 --- a/app/src/main/assets/header_order.json +++ /dev/null @@ -1,106 +0,0 @@ -[ - "client_code", - "questionnaire_1_demographic_information", - "questionnaire_1_demographic_information-coach_code", - "questionnaire_1_demographic_information-consent_instruction", - "questionnaire_1_demographic_information-no_consent_entered", - "questionnaire_1_demographic_information-accommodation", - "questionnaire_1_demographic_information-other_accommodation", - "questionnaire_1_demographic_information-client_code_entry_question", - "questionnaire_1_demographic_information-age", - "questionnaire_1_demographic_information-gender", - "questionnaire_1_demographic_information-country_of_origin", - "questionnaire_1_demographic_information-departure_country", - "questionnaire_1_demographic_information-since_in_germany", - "questionnaire_1_demographic_information-living_situation", - "questionnaire_1_demographic_information-number_family_members", - "questionnaire_1_demographic_information-languages_spoken", - "questionnaire_1_demographic_information-german_skills", - "questionnaire_1_demographic_information-school_years_total", - "questionnaire_1_demographic_information-school_years_origin", - "questionnaire_1_demographic_information-school_years_transit", - "questionnaire_1_demographic_information-school_years_germany", - "questionnaire_1_demographic_information-vocational_training", - "questionnaire_1_demographic_information-provisional_accommodation_since", - "questionnaire_2_rhs", - "questionnaire_2_rhs-coach_code", - "questionnaire_2_rhs-glass_explanation", - "questionnaire_2_rhs-q1_symptom", - "questionnaire_2_rhs-q2_symptom", - "questionnaire_2_rhs-q3_symptom", - "questionnaire_2_rhs-q4_symptom", - "questionnaire_2_rhs-q5_symptom", - "questionnaire_2_rhs-q6_symptom", - "questionnaire_2_rhs-q7_symptom", - "questionnaire_2_rhs-q8_symptom", - "questionnaire_2_rhs-q9_symptom", - "questionnaire_2_rhs-q10_reexperience_trauma", - "questionnaire_2_rhs-q11_physical_reaction", - "questionnaire_2_rhs-q12_emotional_numbness", - "questionnaire_2_rhs-q13_easily_startled", - "questionnaire_2_rhs-q14_intro", - "questionnaire_2_rhs-pain_rating_instruction", - "questionnaire_2_rhs-violence_question_1", - "questionnaire_2_rhs-times_happend", - "questionnaire_2_rhs-age_at_incident", - "questionnaire_2_rhs-conflict_since_arrival", - "questionnaire_2_rhs-times_happend2", - "questionnaire_2_rhs-age_at_incident2", - "questionnaire_2_rhs-asylum_procedure_since", - "questionnaire_3_integration_index", - "questionnaire_3_integration_index-coach_code", - "questionnaire_3_integration_index-feeling_connected_to_germany_question", - "questionnaire_3_integration_index-understanding_political_issues", - "questionnaire_3_integration_index-unexpected_expense_question", - "questionnaire_3_integration_index-dining_with_germans_question", - "questionnaire_3_integration_index-reading_german_articles_question", - "questionnaire_3_integration_index-visiting_doctor_question", - "questionnaire_3_integration_index-feeling_as_outsider_question", - "questionnaire_3_integration_index-recent_occupation_question", - "questionnaire_3_integration_index-discussing_politics_question", - "questionnaire_3_integration_index-contact_with_germans_question", - "questionnaire_3_integration_index-speaking_german_opinion_question", - "questionnaire_3_integration_index-job_search_question", - "questionnaire_4_consultation_results", - "questionnaire_4_consultation_results-coach_code", - "questionnaire_4_consultation_results-date_consultation_health_interview_result", - "questionnaire_4_consultation_results-consultation_decision", - "questionnaire_4_consultation_results-consent_conversation_in_6_months", - "questionnaire_4_consultation_results-participation_in_coaching", - "questionnaire_4_consultation_results-consent_coaching_given", - "questionnaire_4_consultation_results-consent_conversation_in_6_months", - "questionnaire_4_consultation_results-decision_after_reflection_period", - "questionnaire_4_consultation_results-professional_referral", - "questionnaire_4_consultation_results-confidentiality_agreement", - "questionnaire_4_consultation_results-health_insurance_card", - "questionnaire_4_consultation_results-consent_conversation_in_6_months", - "questionnaire_5_final_interview", - "questionnaire_5_final_interview-coach_code", - "questionnaire_5_final_interview-consent_followup_6_months", - "questionnaire_5_final_interview-date_final_interview", - "questionnaire_5_final_interview-amount_nat_appointments", - "questionnaire_5_final_interview-amount_session_flowers", - "questionnaire_5_final_interview-amount_session_stones", - "questionnaire_5_final_interview-termination_nat_coaching", - "questionnaire_5_final_interview-client_canceled_NAT", - "questionnaire_6_follow_up_survey", - "questionnaire_6_follow_up_survey-coach_code", - "questionnaire_6_follow_up_survey-follow_up", - "questionnaire_6_follow_up_survey-special_burden_question", - "questionnaire_6_follow_up_survey-glass_explanation", - "questionnaire_6_follow_up_survey-how_strong_past_month", - "questionnaire_6_follow_up_survey-q14_intro", - "questionnaire_6_follow_up_survey-pain_rating_instruction", - "questionnaire_6_follow_up_survey-feeling_connected_to_germany_question", - "questionnaire_6_follow_up_survey-understanding_political_issues", - "questionnaire_6_follow_up_survey-unexpected_expense_question", - "questionnaire_6_follow_up_survey-dining_with_germans_question", - "questionnaire_6_follow_up_survey-reading_german_articles_question", - "questionnaire_6_follow_up_survey-visiting_doctor_question", - "questionnaire_6_follow_up_survey-feeling_as_outsider_question", - "questionnaire_6_follow_up_survey-recent_occupation_question", - "questionnaire_6_follow_up_survey-discussing_politics_question", - "questionnaire_6_follow_up_survey-contact_with_germans_question", - "questionnaire_6_follow_up_survey-speaking_german_opinion_question", - "questionnaire_6_follow_up_survey-job_search_question" -] diff --git a/app/src/main/assets/header_order.xlsx b/app/src/main/assets/header_order.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..207c3af6b8ca3273aa71609822178907059180e4 GIT binary patch literal 10212 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�k_}O2O0UvADl(1(e&V^zLL@1gfS7p-$z%@U~h*4v1zPv8oNL47E$q8m{AD z`_@94v)(l%&rNVtZh?JFy$ble3mjgj=mV|>S!AO29LeZ?DsjFju8gk7A7vv0DmLS_ z1Hc+r+wOT-R_vZ&8$_RS>{wcLhRd9a@!&5 zYBIHzX-Ch=2Y)%t0p~FEZpT(sD^|Z{to@MHbmKj_*P&S5t)_qPG`Y9JSZi#;qx^(f zZx?$?`=rIeSF;lA00svzA~K;mr53^`#I>3y@fE3C*f8Z~8{!xhfM?=b(fA2KU;X(R z2osQ(3!nQsXfE2ne*Xouyo)vrXCq|87{AAPI{ST2WJ`{dbm}JiJs^+v%ti>gE9{i2 zJA#dhUGr<`5K9_fa3w{Y0jrNiS)kBCp4W3`n|n*+d5ZVXjrue$5b&j`=(OAR0oouQ zOqGv2Amgt&gmX!TuoeNzH2Ailk!d#lXcTXfm8r}?micj^csq@f>@_j8T#ZwTIr7Pg_)l_xZfKyQM&BeXE7fq z8hA;#i!8x!7|RjSVo`e0(z(Qj3NJqbsjqE6!wz5^n9`Gx29Xmzb)#^}N>PPfB8_O- z@polqr=>L$=`==?k&b8#dAM`Sr}c4s$s`Mu?kA5In+r57P^lUM&oow$_=Pz$Bt;z* zH73o!x@R=+?Vk_^Y2?_Ti-RM|$MPsq&n-jp!HOjsn|C#BOa!sl#lj#F1zv?U9(oT1 zRugW8o^6!vC#Lzv61X$p-G%q8^Ys&qL+V~6fD0XK$)s}_(`Z9&(#}vHkLDks^^(7b z?oe=hsY9cB8>m;uhNhw|T`V-*U0gjlEnM8KetLCi?eTvZXsAz!OMa%@$AuTUg7_#q z+-NlKyoc@-ErJBHfM<+xp>A9Fn9WBw;rzJVv}7MGag`aM_953pQDkm}<_gZ@=^GwA zEE>E}b9zGil%knwHnNf)kt!i}B}8J8(5ESPXEavX^^-l5sU&f@rts>T>0Eh5wI11~ zj&lTb&G5$>#)cX4nmdWs;rv#j?XbCW6By~MOce?1Bt&}&FB5=YMM@v|p2Dx9%fjNM zHVRSnUk&c}0B?69<-ZG;xgsBml5bqj&&0J+`2x3^aUXc#%QZp(Sg0*mn@l@^o77vmnE$n9Ci}l zRg2dx{fd615^B$RQyHJRlgs;<1vbXu;yktFd#Egs>@XR0L_Q;0f9$6wUb=kAD7CyP z@knwl_>b$#P<iRzrLSNaxkKAO{pZhT)A;3lIvwRW62#qZw z>fnAjxiW&wI$k?=L(wzzXCr>CD1;WtFO%U|y?M_WI137DIRqRE_>;yzX}mmTA|ys2 zRVGZ2^vzzO$csfEj+ne6Dg+ovxfNkyP4Rsq!4d9>V}~O!f2yrsD%SyO?!_JnOJgZ- zrgr|OIZ)+JP*)OUBunL&yJLeRfKU{1R=G?Zv+OoG9-=y8YakJr@8W<2-fO7qYK*!{ zz3H99FZmLiX*w5UQR|mzX{;<*yr8T@t^q~PNo$0>y+&T$2J5;-9 zHCQzddB?wN_hNP@SM)42ae9jG20lQn|A@$cm!hJ1J>wj$UR3CDFZ#L*jq^%-Y@?a_ zO35{((wa4;aC6)T%@QFPxYUv`cL~0yQ?&m|El1xJ3sy-__NJ`+QXmrf&4D`do}zI(N1W9 z_FHZIcj3PlA^#ACfdQfX68`^`B!AcQdu8emP0eWk_b2{Vr}|yX?t#>{e*~((i~hd+|3ef98W8*x{XH1?UBh3!=pXXXD47Zd=5Idr nck#c{-=D=(pc(c56aP0EYA7Q@XBP$r75WK? = null fun setup() { @@ -106,7 +106,7 @@ class DatabaseButtonHandler( /** * Erzeugt eine Excel-Datei (ClientHeaders.xlsx) mit: - * - Zeile 1: Spalten-IDs (header_order.json) + * - Zeile 1: Spalten-IDs (header_order.json / header_order.xlsx) * - Zeile 2: ENGLISCHE Fragen/Beschriftungen zu jeder ID (rein EN, keine IDs mehr) * - Ab Zeile 3: pro Client die Werte ("Done"/"Not Done"/Antwort oder "None") */ @@ -156,8 +156,7 @@ class DatabaseButtonHandler( id in questionnaireIdSet -> if (statusMap[id] == true) "Done" else "Not Done" else -> answerMap[id]?.takeIf { it.isNotBlank() } ?: "None" } - // Für Export in EN lokalisieren; Done/Not Done/None bleiben unverändert. - val out = localizeForExportEn(id, raw) + val out = localizeForExportEn(id, raw) // Export immer EN (Done/Not Done/None bleiben) row.createCell(c++).setCellValue(out) } } @@ -185,31 +184,24 @@ class DatabaseButtonHandler( // Liefert einen englischen, lesbaren Fragetext/Titel zu einer ID (nur EN, keine IDs). private suspend fun englishQuestionForId(id: String, questionnaireIdSet: Set): String { - // 1) Spezielle Fälle if (id == "client_code") return "Client code" if (id in questionnaireIdSet && !id.contains('-')) return "Questionnaire status" - // 2) Versuch: LanguageManager EN für die ID localizeEnglishNoBrackets(id)?.let { lm -> if (!looksLikeId(lm, id)) return lm } - // 3) Falls ID wie "...-field_name": nur den Feldteil humanisieren val fieldPart = id.substringAfterLast('-', id) localizeEnglishNoBrackets(fieldPart)?.let { lm -> if (!looksLikeId(lm, fieldPart)) return lm } - // 4) Humanisierte englische Fallbacks aus der ID (Titel-Case, Unterstriche → Leerzeichen) val pretty = humanizeIdToEnglish(fieldPart) if (pretty.isNotBlank()) return pretty - - // 5) Letzter Fallback: „Question“ return "Question" } private fun looksLikeId(text: String, originalId: String): Boolean { - // sieht wie die ID aus (nahezu identisch oder nur Klammern/Formatierung) val normText = text.lowercase().replace(Regex("[^a-z0-9]+"), "_").trim('_') val normId = originalId.lowercase().replace(Regex("[^a-z0-9]+"), "_").trim('_') return normText == normId @@ -217,11 +209,10 @@ class DatabaseButtonHandler( private fun humanizeIdToEnglish(source: String): String { val s = source - .replace(Regex("^questionnaire_\\d+_"), "") // Präfix entfernen + .replace(Regex("^questionnaire_\\d+_"), "") .replace('_', ' ') .trim() if (s.isBlank()) return s - // Title Case return s.split(Regex("\\s+")).joinToString(" ") { it.lowercase().replaceFirstChar { c -> c.titlecase() } } } @@ -302,39 +293,50 @@ class DatabaseButtonHandler( } } + // Farben (Material-like) + val lightGreen = 0xFFC8E6C9.toInt() // beantwortet (nicht-Fragebogen) + val lightRed = 0xFFFFCDD2.toInt() // unbeantwortet (nicht-Fragebogen) + val doneGreen = 0xFF4CAF50.toInt() // Fragebogen: Done (BLEIBT) + val notRed = 0xFFF44336.toInt() // Fragebogen: Not Done (BLEIBT) + // Tabelle 2 (Header-Liste) val orderedIds = loadOrderedIds() orderedIds.forEachIndexed { idx, id -> var rowBgColor: Int? = null - val darkGray = 0xFFBDBDBD.toInt() val raw: String - val bgColorForCells: Int? + val cellBgForQuestionnaire: Int? if (id == "client_code") { + // client_code bleibt unmarkiert raw = clientCode - bgColorForCells = null + cellBgForQuestionnaire = null } else if (id in questionnaireIdSet) { + // Fragebogenstatus: wie bisher grün/rot NUR für #, ID, Wert raw = if (statusMap[id] == true) "Done" else "Not Done" - bgColorForCells = if (raw == "Done") 0xFF4CAF50.toInt() else 0xFFF44336.toInt() + cellBgForQuestionnaire = if (raw == "Done") doneGreen else notRed } else { + // Normale Frage: raw = answerMap[id]?.takeIf { it.isNotBlank() } ?: "None" - bgColorForCells = null - if (raw == "None") rowBgColor = darkGray + cellBgForQuestionnaire = null + // NEU: hellgrün wenn beantwortet, hellrot wenn None + rowBgColor = if (raw == "None") lightRed else lightGreen } val display = localizeHeaderValue(id, raw) - val cellBg = if (bgColorForCells != null) - mapOf(0 to bgColorForCells, 1 to bgColorForCells, 2 to bgColorForCells) - else emptyMap() + // Für Fragebögen: "#"(0), ID(1), Wert(2) farbig hinterlegen – wie gehabt + val cellBgOverrides = + if (cellBgForQuestionnaire != null) + mapOf(0 to cellBgForQuestionnaire, 1 to cellBgForQuestionnaire, 2 to cellBgForQuestionnaire) + else emptyMap() addRow( table = tableOrdered, cells = listOf((idx + 1).toString(), id, display), colorOverrides = emptyMap(), - rowBgColor = rowBgColor, - cellBgOverrides = cellBg + rowBgColor = rowBgColor, // greift für normale Fragen (ganze Zeile inkl. #) + cellBgOverrides = cellBgOverrides // greift für Fragebögen (nur 3 Zellen) ) } } @@ -388,10 +390,20 @@ class DatabaseButtonHandler( } // --------------------------- - // assets/header_order.json laden (mit Cache) + // ordered_ids aus XLSX/JSON laden (mit Cache) // --------------------------- private fun loadOrderedIds(): List { orderedIdsCache?.let { return it } + + val fromXlsx = runCatching { loadOrderedIdsFromExcel("header_order.xlsx") } + .onFailure { e -> Log.w(tag, "header_order.xlsx konnte nicht gelesen werden: ${e.message}") } + .getOrElse { emptyList() } + + if (fromXlsx.isNotEmpty()) { + orderedIdsCache = fromXlsx + return fromXlsx + } + return try { val stream = activity.assets.open("header_order.json") val json = stream.readBytes().toString(Charset.forName("UTF-8")) @@ -400,12 +412,49 @@ class DatabaseButtonHandler( orderedIdsCache = list list } catch (e: Exception) { - Log.e(tag, "header_order.json konnte nicht geladen werden: ${e.message}") - Toast.makeText(activity, "header_order.json fehlt oder ist ungültig", Toast.LENGTH_LONG).show() + Log.e(tag, "Weder header_order.xlsx noch header_order.json verfügbar/gültig: ${e.message}") + Toast.makeText(activity, "Keine Header-Vorlage gefunden", Toast.LENGTH_LONG).show() emptyList() } } + private fun loadOrderedIdsFromExcel(assetFileName: String): List { + activity.assets.open(assetFileName).use { input -> + XSSFWorkbook(input).use { wb -> + val sheet = wb.getSheetAt(0) ?: return emptyList() + val row = sheet.getRow(0) ?: return emptyList() + + val first = row.firstCellNum.toInt() + val last = row.lastCellNum.toInt() // exklusiv + val out = mutableListOf() + + for (i in first until last) { + val cell = row.getCell(i) ?: continue + val value = getCellAsString(cell).trim() + if (value.isEmpty()) continue + if (i == first && value == "#") continue // „#“ in Spalte 0 ignorieren + out.add(value) + } + return out + } + } + } + + private fun getCellAsString(cell: org.apache.poi.ss.usermodel.Cell): String = + when (cell.cellType) { + org.apache.poi.ss.usermodel.CellType.STRING -> cell.stringCellValue + org.apache.poi.ss.usermodel.CellType.NUMERIC -> + if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) + cell.dateCellValue.time.toString() + else { + val n = cell.numericCellValue + if (n % 1.0 == 0.0) n.toLong().toString() else n.toString() + } + org.apache.poi.ss.usermodel.CellType.BOOLEAN -> cell.booleanCellValue.toString() + org.apache.poi.ss.usermodel.CellType.FORMULA -> cell.richStringCellValue.string + else -> "" + } + // --------------------------- // Hilfsfunktionen (Lokalisierung & UI) // --------------------------- @@ -468,6 +517,9 @@ class DatabaseButtonHandler( return raw } + // --------------------------- + // UI-Helfer + // --------------------------- private fun addHeaderRow(table: TableLayout, labels: List) { val row = TableRow(activity) labels.forEach { label -> row.addView(makeHeaderCell(label)) }