Change the parsing mechanist to allow more flexibility.
This commit is contained in:
parent
f5c10c5154
commit
fb31de8130
19 changed files with 577 additions and 140 deletions
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue