finished header and changed colors.
This commit is contained in:
@ -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"
|
||||
]
|
||||
BIN
app/src/main/assets/header_order.xlsx
Normal file
BIN
app/src/main/assets/header_order.xlsx
Normal file
Binary file not shown.
@ -26,7 +26,7 @@ class DatabaseButtonHandler(
|
||||
private val uiScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||
private val tag = "DatabaseButtonHandler"
|
||||
|
||||
// Cache für geladene IDs aus assets/header_order.json
|
||||
// Cache für geladene IDs aus assets/header_order.json oder header_order.xlsx
|
||||
private var orderedIdsCache: List<String>? = 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>): 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<String> {
|
||||
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<String> {
|
||||
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<String>()
|
||||
|
||||
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<String>) {
|
||||
val row = TableRow(activity)
|
||||
labels.forEach { label -> row.addView(makeHeaderCell(label)) }
|
||||
|
||||
Reference in New Issue
Block a user