finished header and changed colors.
This commit is contained in:
@ -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