Change the parsing mechanist to allow more flexibility.

This commit is contained in:
Andres Gomez, Thomas (ITDV CC) - AF (ext) 2023-07-17 10:56:38 +02:00
parent f5c10c5154
commit fb31de8130
19 changed files with 577 additions and 140 deletions

View file

@ -27,7 +27,7 @@ class AuthenticationRepository @Inject constructor(
.setBackOff(ExponentialBackOff())
credential.selectedAccount = signInCredential.value?.let {
Account(it.id, "google")
Account(it.id, ACCOUNT_TYPE)
}
return credential
@ -40,8 +40,8 @@ class AuthenticationRepository @Inject constructor(
}
companion object {
private const val ACCOUNT_TYPE = "google"
private val capabilities = listOf(
// SheetsScopes.SPREADSHEETS,
SheetsScopes.SPREADSHEETS_READONLY,
)
}

View file

@ -28,7 +28,6 @@ class LexiconRepository @Inject constructor(
GsonFactory(),
authenticationRepository.credential,
)
.setApplicationName("RP-Lexique")
.build()
else -> null
@ -45,7 +44,7 @@ class LexiconRepository @Inject constructor(
throw ServiceNotReady()
} else {
withContext(Dispatchers.IO) {
val request = service.spreadsheets().values().get(ID, LEXIQUE)
val request = service.spreadsheets().values().get(Sheet.ID, Sheet.LEXIQUE)
val data = request.execute()
updateData(data = data)
}
@ -55,53 +54,98 @@ class LexiconRepository @Inject constructor(
@Throws(IncompatibleSheetStructure::class)
private fun updateData(data: ValueRange?) {
val sheet = data?.values?.sheet()
var sheetStructure: Map<String, Int>? = null
val lexicon: List<Lexicon> = sheet?.mapIndexedNotNull { index, row ->
if (index == 0) {
checkSheetStructure(firstRow = row)
null
} else {
parseCharacterRow(index = index - 1, row = row as? List<Any>?)
when {
index == 0 -> {
sheetStructure = checkSheetStructure(firstRow = row)
null
}
row is List<*> -> parseCharacterRow(
sheetStructure = sheetStructure,
index = index - 1,
row = row,
)
else -> null
}
} ?: emptyList()
_data.tryEmit(lexicon)
}
@Throws(IncompatibleSheetStructure::class)
private fun checkSheetStructure(firstRow: Any?) {
when {
firstRow !is ArrayList<*> -> throw IncompatibleSheetStructure("First row is not a List: $firstRow")
firstRow.size < 7 -> throw IncompatibleSheetStructure("First row have not enough column: ${firstRow.size}, $firstRow")
firstRow.size > 7 -> throw IncompatibleSheetStructure("First row have too mush columns: ${firstRow.size}, $firstRow")
else -> {
for (index in 0..6) {
if (columns[index] != firstRow[index]) {
throw IncompatibleSheetStructure("Column at index:$index should be ${columns[index]} but was ${firstRow[index]}")
}
}
private fun checkSheetStructure(firstRow: Any?): HashMap<String, Int> {
// check if the row is a list
if (firstRow !is ArrayList<*>) {
throw IncompatibleSheetStructure("First row is not a List: $firstRow")
}
// parse the first line to find element that we recognize.
val sheetStructure = hashMapOf<String, Int>()
firstRow.forEachIndexed { index, cell ->
when (cell as? String) {
Sheet.NAME,
Sheet.DIMINUTIVE,
Sheet.GENDER,
Sheet.RACE,
Sheet.PORTRAIT,
Sheet.DESCRIPTION,
Sheet.HISTORY -> sheetStructure[cell] = index
}
}
// check if we found everything we need.
when {
sheetStructure.size < Sheet.KNOWN_COLUMN -> throw IncompatibleSheetStructure(
message = "Sheet header row does not have enough column: ${firstRow.size}.\nstructure: $firstRow\nheader: $sheetStructure"
)
sheetStructure.size > Sheet.KNOWN_COLUMN -> throw IncompatibleSheetStructure(
message = "Sheet header row does have too mush columns: ${firstRow.size}.\nstructure: $firstRow\nheader: $sheetStructure"
)
}
return sheetStructure
}
private fun parseCharacterRow(index: Int, row: List<Any>?): Lexicon? {
val name = row?.getOrNull(0) as? String
val diminutive = row?.getOrNull(1) as? String?
val gender = row?.getOrNull(2) as? String?
val race = row?.getOrNull(3) as? String?
val portrait = row?.getOrNull(4) as? String?
val description = row?.getOrNull(5) as? String?
val history = row?.getOrNull(6) as? String?
private fun parseCharacterRow(
sheetStructure: Map<String, Int>?,
index: Int,
row: List<*>?,
): Lexicon? {
val name = row?.getOrNull(sheetStructure.name) as? String
val diminutive = row?.getOrNull(sheetStructure.diminutive) as? String?
val gender = row?.getOrNull(sheetStructure.gender) as? String?
val race = row?.getOrNull(sheetStructure.race) as? String?
val portrait = row?.getOrNull(sheetStructure.portrait) as? String?
val description = row?.getOrNull(sheetStructure.description) as? String?
val history = row?.getOrNull(sheetStructure.history) as? String?
return if (name != null) {
Lexicon(
id = index,
name = name,
diminutive = diminutive?.takeIf { it.isNotBlank() },
gender = when (gender) {
"Male" -> Lexicon.Gender.MALE
"Femelle" -> Lexicon.Gender.FEMALE
gender = when (gender?.takeIf { it.isNotBlank() }) {
Gender.MALE -> Lexicon.Gender.MALE
Gender.FEMALE -> Lexicon.Gender.FEMALE
else -> Lexicon.Gender.UNDETERMINED
},
race = race?.takeIf { it.isNotBlank() },
race = when (race?.takeIf { it.isNotBlank() }) {
Race.ELF -> Lexicon.Race.ELF
Race.HALFLING -> Lexicon.Race.HALFLING
Race.HUMAN -> Lexicon.Race.HUMAN
Race.DWARF -> Lexicon.Race.DWARF
Race.HALF_ELF -> Lexicon.Race.HALF_ELF
Race.HALF_ORC -> Lexicon.Race.HALF_ORC
Race.DRAGONBORN -> Lexicon.Race.DRAGONBORN
Race.GNOME -> Lexicon.Race.GNOME
Race.TIEFLING -> Lexicon.Race.TIEFLING
Race.AARAKOCRA -> Lexicon.Race.AARAKOCRA
Race.GENASI -> Lexicon.Race.GENASI
Race.DEEP_GNOME -> Lexicon.Race.DEEP_GNOME
Race.GOLIATH -> Lexicon.Race.GOLIATH
else -> Lexicon.Race.UNDETERMINED
},
portrait = portrait?.split("\n")?.mapNotNull { it.toUriOrNull() } ?: emptyList(),
description = description?.takeIf { it.isNotBlank() },
history = history?.takeIf { it.isNotBlank() },
@ -124,16 +168,53 @@ class LexiconRepository @Inject constructor(
null
}
private val Map<String, Int>?.name: Int get() = this?.getValue(Sheet.NAME) ?: 0
private val Map<String, Int>?.diminutive: Int get() = this?.getValue(Sheet.DIMINUTIVE) ?: 1
private val Map<String, Int>?.gender: Int get() = this?.getValue(Sheet.GENDER) ?: 2
private val Map<String, Int>?.race: Int get() = this?.getValue(Sheet.RACE) ?: 3
private val Map<String, Int>?.portrait: Int get() = this?.getValue(Sheet.PORTRAIT) ?: 4
private val Map<String, Int>?.description: Int get() = this?.getValue(Sheet.DESCRIPTION) ?: 5
private val Map<String, Int>?.history: Int get() = this?.getValue(Sheet.HISTORY) ?: 6
class ServiceNotReady : Exception()
class IncompatibleSheetStructure(message: String?) : Exception(message)
companion object {
const val TAG = "LexiconRepository"
}
private object Sheet {
const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs"
const val LEXIQUE = "Lexique"
const val KNOWN_COLUMN = 7
const val NAME = "Nom"
const val DIMINUTIVE = "Diminutif"
const val GENDER = "Sexe"
const val RACE = "Race"
const val PORTRAIT = "Portrait"
const val DESCRIPTION = "Description"
const val HISTORY = "Histoire"
}
val columns =
listOf("Nom", "Diminutif", "Sexe", "Race", "Portrait", "Description", "Histoire")
private object Gender {
const val MALE = "Male"
const val FEMALE = "Femelle"
}
private object Race {
const val ELF = "Elfe"
const val HALFLING = "Halfelin"
const val HUMAN = "Humain"
const val DWARF = "Nain"
const val HALF_ELF = "Demi-Elfe"
const val HALF_ORC = "Demi-Orc"
const val DRAGONBORN = "Drakéide"
const val GNOME = "Gnome"
const val TIEFLING = "Tieffelin"
const val AARAKOCRA = "Aarakocra"
const val GENASI = "Génasi"
const val DEEP_GNOME = "Gnome des Profondeurs"
const val GOLIATH = "Goliath"
}
}