From 87a1471efe05ac0b5b9aee60f35eefe4829f6e1b Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Thu, 3 Aug 2023 16:11:20 +0200 Subject: [PATCH] Refactor repositories by adding parsers. Add character sheet poc. --- .../com/pixelized/rplexicon/MainActivity.kt | 2 + .../rplexicon/facotry/GenderParser.kt | 20 ++ .../rplexicon/facotry/LexiconParser.kt | 83 +++++++++ .../rplexicon/facotry/LocationParser.kt | 54 ++++++ .../rplexicon/facotry/MarqueeParser.kt | 58 ++++++ .../rplexicon/facotry/PortraitParser.kt | 16 ++ .../rplexicon/facotry/QuestParser.kt | 81 ++++++++ .../pixelized/rplexicon/facotry/RaceParser.kt | 42 +++++ .../pixelized/rplexicon/facotry/RollParser.kt | 49 +++++ .../rplexicon/model/CharacterSheet.kt | 84 +++++++++ .../com/pixelized/rplexicon/model/Location.kt | 22 +++ .../model/{QuestEntry.kt => Quest.kt} | 0 .../repository/CharacterSheetRepository.kt | 120 ++++++++++++ .../GoogleSheetServiceRepository.kt | 44 +++++ .../rplexicon/repository/LexiconRepository.kt | 173 +----------------- .../repository/LocationRepository.kt | 66 +++++++ .../rplexicon/repository/QuestRepository.kt | 133 +------------- 17 files changed, 761 insertions(+), 286 deletions(-) create mode 100644 app/src/main/java/com/pixelized/rplexicon/facotry/GenderParser.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/facotry/LexiconParser.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/facotry/LocationParser.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/facotry/MarqueeParser.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/facotry/PortraitParser.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/facotry/QuestParser.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/facotry/RaceParser.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/facotry/RollParser.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/model/CharacterSheet.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/model/Location.kt rename app/src/main/java/com/pixelized/rplexicon/model/{QuestEntry.kt => Quest.kt} (100%) create mode 100644 app/src/main/java/com/pixelized/rplexicon/repository/CharacterSheetRepository.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/repository/GoogleSheetServiceRepository.kt create mode 100644 app/src/main/java/com/pixelized/rplexicon/repository/LocationRepository.kt diff --git a/app/src/main/java/com/pixelized/rplexicon/MainActivity.kt b/app/src/main/java/com/pixelized/rplexicon/MainActivity.kt index bb4d5bf..d164a5d 100644 --- a/app/src/main/java/com/pixelized/rplexicon/MainActivity.kt +++ b/app/src/main/java/com/pixelized/rplexicon/MainActivity.kt @@ -2,6 +2,7 @@ package com.pixelized.rplexicon import android.app.Activity import android.os.Bundle +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.WindowInsets @@ -19,6 +20,7 @@ import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.Modifier import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.WindowCompat +import com.pixelized.rplexicon.facotry.RollParser import com.pixelized.rplexicon.ui.navigation.ScreenNavHost import com.pixelized.rplexicon.ui.theme.LexiconTheme import dagger.hilt.android.AndroidEntryPoint diff --git a/app/src/main/java/com/pixelized/rplexicon/facotry/GenderParser.kt b/app/src/main/java/com/pixelized/rplexicon/facotry/GenderParser.kt new file mode 100644 index 0000000..f345a60 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/facotry/GenderParser.kt @@ -0,0 +1,20 @@ +package com.pixelized.rplexicon.facotry + +import com.pixelized.rplexicon.model.Lexicon +import javax.inject.Inject + +class GenderParser @Inject constructor() { + + fun parse(gender: String?): Lexicon.Gender { + return when (gender?.takeIf { it.isNotBlank() }) { + Gender.MALE -> Lexicon.Gender.MALE + Gender.FEMALE -> Lexicon.Gender.FEMALE + else -> Lexicon.Gender.UNDETERMINED + } + } + + private object Gender { + const val MALE = "Male" + const val FEMALE = "Femelle" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/facotry/LexiconParser.kt b/app/src/main/java/com/pixelized/rplexicon/facotry/LexiconParser.kt new file mode 100644 index 0000000..bb5f299 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/facotry/LexiconParser.kt @@ -0,0 +1,83 @@ +package com.pixelized.rplexicon.facotry + +import com.google.api.services.sheets.v4.model.ValueRange +import com.pixelized.rplexicon.model.Lexicon +import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure +import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure +import com.pixelized.rplexicon.utilitary.extentions.sheet +import javax.inject.Inject + +class LexiconParser @Inject constructor( + private val portraitParser: PortraitParser, + private val genderParser: GenderParser, + private val raceParser: RaceParser, +) { + + @Throws(IncompatibleSheetStructure::class) + fun parse(data: ValueRange): List { + val sheet = data.values.sheet() + lateinit var sheetStructure: Map + var id = 0 + + return sheet?.mapIndexedNotNull { index, row -> + when { + index == 0 -> { + sheetStructure = row.checkSheetStructure(model = COLUMNS) + null + } + + row is List<*> -> { + 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? + val tags = row.getOrNull(sheetStructure.tags) as? String? + + if (name != null) { + Lexicon( + id = id++, + sheetIndex = index, + name = name, + diminutive = diminutive?.takeIf { it.isNotBlank() }, + gender = genderParser.parse(gender), + race = raceParser.parser(race), + portrait = portraitParser.parse(portrait), + description = description?.takeIf { it.isNotBlank() }, + history = history?.takeIf { it.isNotBlank() }, + tags = tags?.takeIf { it.isNotBlank() }, + ) + } else { + null + } + } + + else -> null + } + } ?: emptyList() + } + + private val Map.name: Int get() = getValue(COLUMNS[0]) + private val Map.diminutive: Int get() = getValue(COLUMNS[1]) + private val Map.gender: Int get() = getValue(COLUMNS[2]) + private val Map.race: Int get() = getValue(COLUMNS[3]) + private val Map.portrait: Int get() = getValue(COLUMNS[4]) + private val Map.description: Int get() = getValue(COLUMNS[5]) + private val Map.history: Int get() = getValue(COLUMNS[6]) + private val Map.tags: Int get() = getValue(COLUMNS[7]) + + companion object { + private val COLUMNS = listOf( + "Nom", + "Diminutif", + "Sexe", + "Race", + "Portrait", + "Description", + "Histoire", + "Mots clés", + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/facotry/LocationParser.kt b/app/src/main/java/com/pixelized/rplexicon/facotry/LocationParser.kt new file mode 100644 index 0000000..d5b7a27 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/facotry/LocationParser.kt @@ -0,0 +1,54 @@ +package com.pixelized.rplexicon.facotry + +import android.net.Uri +import com.google.api.services.sheets.v4.model.ValueRange +import com.pixelized.rplexicon.model.Location +import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure +import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure +import com.pixelized.rplexicon.utilitary.extentions.sheet +import javax.inject.Inject + +class LocationParser @Inject constructor() { + + @Throws(IncompatibleSheetStructure::class) + fun parse(data: ValueRange): List { + var id = 0 + var sheetStructure: Map? = null + + return data.values.sheet()?.mapIndexedNotNull { index, item -> + when { + index == 0 -> { + sheetStructure = item.checkSheetStructure(COLUMNS) + null + } + + item is List<*> -> { + val name = item.getOrNull(sheetStructure.name) as? String + val uri = item.getOrNull(sheetStructure.uri)?.toString()?.let { Uri.parse(it) } + + if (name != null && uri != null) { + Location( + id = id++, + sheetIndex = index, + name = name, + uri = uri, + ) + } else { + null + } + } + + else -> null + } + } ?: emptyList() + } + + private val Map?.name: Int get() = this?.getValue(COLUMNS[0]) ?: 0 + private val Map?.uri: Int get() = this?.getValue(COLUMNS[1]) ?: 1 + + companion object { + private val COLUMNS = listOf( + "nom", "carte" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/facotry/MarqueeParser.kt b/app/src/main/java/com/pixelized/rplexicon/facotry/MarqueeParser.kt new file mode 100644 index 0000000..6f0ec58 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/facotry/MarqueeParser.kt @@ -0,0 +1,58 @@ +package com.pixelized.rplexicon.facotry + +import com.google.api.services.sheets.v4.model.ValueRange +import com.pixelized.rplexicon.model.Location +import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure +import com.pixelized.rplexicon.utilitary.extentions.sheet +import javax.inject.Inject + +class MarqueeParser @Inject constructor() { + + fun parse(data: ValueRange): List { + val sheet = data.values.sheet() + lateinit var structure: Map + + return sheet?.mapIndexedNotNull { index, item -> + when { + index == 0 -> { + structure = item.checkSheetStructure(model = COLUMNS) + null + } + + item is List<*> -> { + val map = item.getOrNull(structure.map) as? String + val name = item.getOrNull(structure.name) as? String + val x = (item.getOrNull(structure.x) as? String)?.toFloatOrNull() + val y = (item.getOrNull(structure.y) as? String)?.toFloatOrNull() + val description = item.getOrNull(structure.description) as? String? + + if (map != null && name != null && x != null && y != null) { + Location.Marquee( + map = map, + name = name, + x = x, + y = y, + description = description, + ) + } else { + null + } + } + + else -> null + } + } ?: emptyList() + } + + private val Map.map: Int get() = getValue(COLUMNS[0]) + private val Map.name: Int get() = getValue(COLUMNS[1]) + private val Map.x: Int get() = getValue(COLUMNS[2]) + private val Map.y: Int get() = getValue(COLUMNS[3]) + private val Map.description: Int get() = getValue(COLUMNS[4]) + + companion object { + private val COLUMNS = listOf( + "carte", "nom", "x", "y", "description" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/facotry/PortraitParser.kt b/app/src/main/java/com/pixelized/rplexicon/facotry/PortraitParser.kt new file mode 100644 index 0000000..3e3521d --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/facotry/PortraitParser.kt @@ -0,0 +1,16 @@ +package com.pixelized.rplexicon.facotry + +import android.net.Uri +import com.pixelized.rplexicon.utilitary.extentions.toUriOrNull +import javax.inject.Inject + +class PortraitParser @Inject constructor() { + + fun parse(value: String?): List { + return value?.split(SEPARATOR)?.mapNotNull { it.toUriOrNull() } ?: emptyList() + } + + companion object { + private const val SEPARATOR = "\n" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/facotry/QuestParser.kt b/app/src/main/java/com/pixelized/rplexicon/facotry/QuestParser.kt new file mode 100644 index 0000000..7cd92ab --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/facotry/QuestParser.kt @@ -0,0 +1,81 @@ +package com.pixelized.rplexicon.facotry + + +import com.google.api.services.sheets.v4.model.ValueRange +import com.pixelized.rplexicon.model.QuestEntry +import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure +import com.pixelized.rplexicon.utilitary.extentions.sheet +import com.pixelized.rplexicon.utilitary.extentions.toUriOrNull +import javax.inject.Inject + +class QuestParser @Inject constructor() { + + fun parse(value: ValueRange): List { + val sheet = value.values.sheet() + lateinit var structure: Map + + return sheet?.mapIndexedNotNull { index, item -> + when { + index == 0 -> { + structure = item.checkSheetStructure(model = COLUMNS) + null + } + + item is List<*> -> { + val title = item.getOrNull(structure.title) as? String + val subtitle = item.getOrNull(structure.subtitle) as? String? + val complete = item.getOrNull(structure.complete) as? String? + val questGiver = item.getOrNull(structure.questGiver) as? String? + val area = item.getOrNull(structure.area) as? String? + val groupReward = item.getOrNull(structure.groupReward) as? String? + val individualReward = item.getOrNull(structure.individualReward) as? String? + val description = item.getOrNull(structure.description) as? String + val background = item.getOrNull(structure.background) as? String? + + if (title?.isNotEmpty() == true && description?.isNotEmpty() == true) { + QuestEntry( + sheetIndex = index, + title = title, + subtitle = subtitle?.takeIf { it.isNotBlank() }, + complete = complete.equals("TRUE", ignoreCase = true), + questGiver = questGiver?.takeIf { it.isNotBlank() }, + area = area?.takeIf { it.isNotBlank() }, + groupReward = groupReward?.takeIf { it.isNotBlank() }, + individualReward = individualReward?.takeIf { it.isNotBlank() }, + description = description, + background = background?.toUriOrNull(), + ) + } else { + null + } + } + + else -> null + } + } ?: emptyList() + } + + private val Map.title: Int get() = getValue(COLUMNS[0]) + private val Map.subtitle: Int get() = getValue(COLUMNS[1]) + private val Map.complete: Int get() = getValue(COLUMNS[2]) + private val Map.questGiver: Int get() = getValue(COLUMNS[3]) + private val Map.area: Int get() = getValue(COLUMNS[4]) + private val Map.groupReward: Int get() = getValue(COLUMNS[5]) + private val Map.individualReward: Int get() = getValue(COLUMNS[6]) + private val Map.description: Int get() = getValue(COLUMNS[7]) + private val Map.background: Int get() = getValue(COLUMNS[8]) + + companion object { + private val COLUMNS = listOf( + "Titre", + "Sous Titre", + "Compléter", + "Commanditaire", + "Lieu", + "Récompense de groupe", + "Récompense individuelle", + "Description", + "fond" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/facotry/RaceParser.kt b/app/src/main/java/com/pixelized/rplexicon/facotry/RaceParser.kt new file mode 100644 index 0000000..8c01fa4 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/facotry/RaceParser.kt @@ -0,0 +1,42 @@ +package com.pixelized.rplexicon.facotry + +import com.pixelized.rplexicon.model.Lexicon +import javax.inject.Inject + +class RaceParser @Inject constructor() { + + fun parser(race: String?): Lexicon.Race { + return 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 + } + } + + 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" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/facotry/RollParser.kt b/app/src/main/java/com/pixelized/rplexicon/facotry/RollParser.kt new file mode 100644 index 0000000..152049d --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/facotry/RollParser.kt @@ -0,0 +1,49 @@ +package com.pixelized.rplexicon.facotry + +import com.pixelized.rplexicon.model.CharacterSheet +import com.pixelized.rplexicon.model.Roll +import javax.inject.Inject + +class RollParser @Inject constructor() { + private val diceRegex = Regex("(\\d+)d(\\d+)") + private val bonusRegex = Regex("(?:[a-zA-Z]|\\?)[a-zA-Z]+") + + fun parseRoll(characterSheet: CharacterSheet, value: String?): Roll { + val roll = value?.split(";") + + val label = roll?.getOrNull(0) + val (dices, bonus) = roll?.getOrNull(1)?.let { item -> + val dices = diceRegex.findAll(item).toList().map { it.parseDice() } + val bonus = bonusRegex.findAll(item).map { bonus -> + Roll.Bonus( + label = bonus.value, + bonus = bonus.value.parseBonus(characterSheet = characterSheet), + ) + } + dices.toList() to bonus.toList() + } ?: (null to null) + + return Roll( + label = label.toString(), + dices = dices ?: emptyList(), + bonus = bonus ?: emptyList(), + ) + } + + private fun String?.parseBonus( + characterSheet: CharacterSheet, + ): Int = when (this?.lowercase()) { + "bonus" -> characterSheet.proficiency + "force" -> characterSheet.strengthBonus + else -> 0 + } + + private fun MatchResult.parseDice(): Roll.Dice { + val (count, faces) = destructured + return Roll.Dice( + label = value, + count = count.toIntOrNull() ?: 0, + faces = faces.toIntOrNull() ?: 0, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/model/CharacterSheet.kt b/app/src/main/java/com/pixelized/rplexicon/model/CharacterSheet.kt new file mode 100644 index 0000000..0ed7116 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/model/CharacterSheet.kt @@ -0,0 +1,84 @@ +package com.pixelized.rplexicon.model + +import androidx.compose.runtime.Stable + +@Stable +data class CharacterSheet( + val hitPoint: Int, // Point de vie + val armorClass: Int, // Classe d'armure + val proficiency: Int, // Bonus de maîtrise + val strength: Int, // Force + val dexterity: Int, // Dextérité + val constitution: Int, // Constitution + val intelligence: Int, // Intelligence + val wisdom: Int, // Sagesse + val charisma: Int, // Charisme + val strengthSavingThrows: Int, // Jet de sauvegarde: Force + val dexteritySavingThrows: Int, // Jet de sauvegarde: Dextérité + val constitutionSavingThrows: Int, // Jet de sauvegarde: Constitution + val intelligenceSavingThrows: Int, // Jet de sauvegarde: Intelligence + val wisdomSavingThrows: Int, // Jet de sauvegarde: Sagesse + val charismaSavingThrows: Int, // Jet de sauvegarde: Charisme + val acrobatics: Int, // DEX, Acrobaties + val animalHandling: Int, // WIS, Arcanes + val arcana: Int, // INT, Athlétisme + val athletics: Int, // STR, Discrétion + val deception: Int, // CHA, Dressage + val history: Int, // INT, Escamotage + val insight: Int, // WIS, Histoire + val intimidation: Int, // CHA, Intimidation + val investigation: Int, // INT, Intuition + val medicine: Int, // WIS, Investigation + val nature: Int, // INT, Médecine + val perception: Int, // WIS, Nature + val performance: Int, // CHA, Perception + val persuasion: Int, // CHA, Persuasion + val religion: Int, // INT, Religion + val sleightOfHand: Int, // DEX, Représentation + val stealth: Int, // DEX, Survie + val survival: Int, // WIS, Tromperie +) { + val proficiencyBonus: Int = kotlin.math.floor(proficiency / 2 - 5f).toInt() + val strengthBonus: Int = kotlin.math.floor(strength / 2 - 5f).toInt() + val dexterityBonus: Int = kotlin.math.floor(dexterity / 2 - 5f).toInt() + val constitutionBonus: Int = kotlin.math.floor(constitution / 2 - 5f).toInt() + val intelligenceBonus: Int = kotlin.math.floor(intelligence / 2 - 5f).toInt() +} + +@Stable +data class Roll( + val label: String, + val dices: List, + val bonus: List, +) { + @Stable + data class Dice( + val label: String, + val count: Int, + val faces: Int, + ) + + @Stable + data class Bonus( + val label: String, + val bonus: Int, + ) +} + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/pixelized/rplexicon/model/Location.kt b/app/src/main/java/com/pixelized/rplexicon/model/Location.kt new file mode 100644 index 0000000..109bd08 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/model/Location.kt @@ -0,0 +1,22 @@ +package com.pixelized.rplexicon.model + +import android.net.Uri +import androidx.compose.runtime.Stable + +@Stable +data class Location( + val id: Int, + val sheetIndex: Int, + val name: String, + val uri: Uri, + val marquees: List, +) { + @Stable + data class Marquee( + val map: String, + val name: String, + val x: Float, + val y: Float, + val description: String?, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/model/QuestEntry.kt b/app/src/main/java/com/pixelized/rplexicon/model/Quest.kt similarity index 100% rename from app/src/main/java/com/pixelized/rplexicon/model/QuestEntry.kt rename to app/src/main/java/com/pixelized/rplexicon/model/Quest.kt diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/CharacterSheetRepository.kt b/app/src/main/java/com/pixelized/rplexicon/repository/CharacterSheetRepository.kt new file mode 100644 index 0000000..6c7c48e --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/repository/CharacterSheetRepository.kt @@ -0,0 +1,120 @@ +package com.pixelized.rplexicon.repository + +import android.util.Log +import com.google.api.services.sheets.v4.model.ValueRange +import com.pixelized.rplexicon.facotry.RollParser +import com.pixelized.rplexicon.model.CharacterSheet +import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure +import com.pixelized.rplexicon.utilitary.exceptions.ServiceNotReady +import com.pixelized.rplexicon.utilitary.extentions.sheet +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CharacterSheetRepository @Inject constructor( + private val googleRepository: GoogleSheetServiceRepository, + private val rollParser: RollParser, +) { + @Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class) + suspend fun fetchCharacterSheet() { + googleRepository.fetch { sheet -> + val request = sheet.get(Sheet.ID, Sheet.CHARACTER_SHEET) + val data = request.execute() + updateData(data = data) + } + } + + @Throws(IncompatibleSheetStructure::class) + private fun updateData(data: ValueRange?) { + val sheet = data?.values?.sheet() + var id = 0 + + val bru = sheet?.map { (it as? List<*>)?.get(1) } + + val characterSheet = CharacterSheet( + hitPoint = (bru?.get(Sheet.HIT_POINT) as? String)?.toIntOrNull() ?: 0, + armorClass = (bru?.get(Sheet.ARMOR_CLASS) as? String)?.toIntOrNull() ?: 0, + proficiency = (bru?.get(Sheet.PROFICIENCY) as? String)?.toIntOrNull() ?: 0, + strength = (bru?.get(Sheet.STRENGTH) as? String)?.toIntOrNull() ?: 0, + dexterity = (bru?.get(Sheet.DEXTERITY) as? String)?.toIntOrNull() ?: 0, + constitution = (bru?.get(Sheet.CONSTITUTION) as? String)?.toIntOrNull() ?: 0, + intelligence = (bru?.get(Sheet.INTELLIGENCE) as? String)?.toIntOrNull() ?: 0, + wisdom = (bru?.get(Sheet.WISDOM) as? String)?.toIntOrNull() ?: 0, + charisma = (bru?.get(Sheet.CHARISMA) as? String)?.toIntOrNull() ?: 0, + strengthSavingThrows = (bru?.get(Sheet.STRENGTH_SAVING_THROWS) as? String)?.toIntOrNull() ?: 0, + dexteritySavingThrows = (bru?.get(Sheet.DEXTERITY_SAVING_THROWS) as? String)?.toIntOrNull() ?: 0, + constitutionSavingThrows = (bru?.get(Sheet.CONSTITUTION_SAVING_THROWS) as? String)?.toIntOrNull() ?: 0, + intelligenceSavingThrows = (bru?.get(Sheet.INTELLIGENCE_SAVING_THROWS) as? String)?.toIntOrNull() ?: 0, + wisdomSavingThrows = (bru?.get(Sheet.WISDOM_SAVING_THROWS) as? String)?.toIntOrNull() ?: 0, + charismaSavingThrows = (bru?.get(Sheet.CHARISMA_SAVING_THROWS) as? String)?.toIntOrNull() ?: 0, + acrobatics = (bru?.get(Sheet.ACROBATICS) as? String)?.toIntOrNull() ?: 0, + animalHandling = (bru?.get(Sheet.ANIMAL_HANDLING) as? String)?.toIntOrNull() ?: 0, + arcana = (bru?.get(Sheet.ARCANA) as? String)?.toIntOrNull() ?: 0, + athletics = (bru?.get(Sheet.ATHLETICS) as? String)?.toIntOrNull() ?: 0, + deception = (bru?.get(Sheet.DECEPTION) as? String)?.toIntOrNull() ?: 0, + history = (bru?.get(Sheet.HISTORY) as? String)?.toIntOrNull() ?: 0, + insight = (bru?.get(Sheet.INSIGHT) as? String)?.toIntOrNull() ?: 0, + intimidation = (bru?.get(Sheet.INTIMIDATION) as? String)?.toIntOrNull() ?: 0, + investigation = (bru?.get(Sheet.INVESTIGATION) as? String)?.toIntOrNull() ?: 0, + medicine = (bru?.get(Sheet.MEDICINE) as? String)?.toIntOrNull() ?: 0, + nature = (bru?.get(Sheet.NATURE) as? String)?.toIntOrNull() ?: 0, + perception = (bru?.get(Sheet.PERCEPTION) as? String)?.toIntOrNull() ?: 0, + performance = (bru?.get(Sheet.PERFORMANCE) as? String)?.toIntOrNull() ?: 0, + persuasion = (bru?.get(Sheet.PERSUASION) as? String)?.toIntOrNull() ?: 0, + religion = (bru?.get(Sheet.RELIGION) as? String)?.toIntOrNull() ?: 0, + sleightOfHand = (bru?.get(Sheet.SLEIGHT_OF_HAND) as? String)?.toIntOrNull() ?: 0, + stealth = (bru?.get(Sheet.STEALTH) as? String)?.toIntOrNull() ?: 0, + survival = (bru?.get(Sheet.SURVIVAL) as? String)?.toIntOrNull() ?: 0, + ) + + val rolls = bru?.subList(fromIndex = 34, bru.size)?.mapNotNull { + rollParser.parseRoll(characterSheet = characterSheet, value = it?.toString()) + } + + Log.e(TAG, characterSheet.toString()) + Log.e(TAG, rolls.toString()) + } + + companion object { + const val TAG = "CharacterSheetRepository" + } + + private object Sheet { + const val ID = "1fHfzeb8y5u9lEQB1iI-jBEhqu7YSip5sAajXcXK7VJ8" + const val CHARACTER_SHEET = "Feuille de personnage" + + const val HIT_POINT = 1 + const val ARMOR_CLASS = 2 + const val PROFICIENCY = 3 + const val STRENGTH = 4 + const val DEXTERITY = 5 + const val CONSTITUTION = 6 + const val INTELLIGENCE = 7 + const val WISDOM = 8 + const val CHARISMA = 9 + const val STRENGTH_SAVING_THROWS = 10 + const val DEXTERITY_SAVING_THROWS = 11 + const val CONSTITUTION_SAVING_THROWS = 12 + const val INTELLIGENCE_SAVING_THROWS = 13 + const val WISDOM_SAVING_THROWS = 14 + const val CHARISMA_SAVING_THROWS = 15 + const val ACROBATICS = 16 + const val ANIMAL_HANDLING = 17 + const val ARCANA = 18 + const val ATHLETICS = 19 + const val DECEPTION = 20 + const val HISTORY = 21 + const val INSIGHT = 22 + const val INTIMIDATION = 23 + const val INVESTIGATION = 24 + const val MEDICINE = 25 + const val NATURE = 26 + const val PERCEPTION = 27 + const val PERFORMANCE = 28 + const val PERSUASION = 29 + const val RELIGION = 30 + const val SLEIGHT_OF_HAND = 31 + const val STEALTH = 32 + const val SURVIVAL = 33 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/GoogleSheetServiceRepository.kt b/app/src/main/java/com/pixelized/rplexicon/repository/GoogleSheetServiceRepository.kt new file mode 100644 index 0000000..490c032 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/repository/GoogleSheetServiceRepository.kt @@ -0,0 +1,44 @@ +package com.pixelized.rplexicon.repository + +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import com.google.api.client.extensions.android.http.AndroidHttp +import com.google.api.client.json.gson.GsonFactory +import com.google.api.services.sheets.v4.Sheets +import com.pixelized.rplexicon.utilitary.exceptions.ServiceNotReady +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class GoogleSheetServiceRepository @Inject constructor( + authenticationRepository: AuthenticationRepository, +) { + private val service: Sheets? by derivedStateOf { + when (authenticationRepository.isAuthenticated.value) { + true -> Sheets + .Builder( + AndroidHttp.newCompatibleTransport(), + GsonFactory(), + authenticationRepository.credential, + ) + .build() + + else -> null + } + } + + @Throws(ServiceNotReady::class) + suspend fun fetch( + lambda: suspend CoroutineScope.(service: Sheets.Spreadsheets.Values) -> Unit, + ) { + when (val service = service) { + null -> throw ServiceNotReady() + else -> withContext(Dispatchers.IO) { + lambda(service.spreadsheets().values()) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/LexiconRepository.kt b/app/src/main/java/com/pixelized/rplexicon/repository/LexiconRepository.kt index c88bf6d..5e4e48a 100644 --- a/app/src/main/java/com/pixelized/rplexicon/repository/LexiconRepository.kt +++ b/app/src/main/java/com/pixelized/rplexicon/repository/LexiconRepository.kt @@ -1,42 +1,20 @@ package com.pixelized.rplexicon.repository -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import com.google.api.client.extensions.android.http.AndroidHttp -import com.google.api.client.json.gson.GsonFactory -import com.google.api.services.sheets.v4.Sheets import com.google.api.services.sheets.v4.model.ValueRange +import com.pixelized.rplexicon.facotry.LexiconParser import com.pixelized.rplexicon.model.Lexicon import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.ServiceNotReady -import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure -import com.pixelized.rplexicon.utilitary.extentions.sheet -import com.pixelized.rplexicon.utilitary.extentions.toUriOrNull -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Singleton @Singleton class LexiconRepository @Inject constructor( - private val authenticationRepository: AuthenticationRepository + private val googleRepository: GoogleSheetServiceRepository, + private val lexiconParser: LexiconParser, ) { - private val sheetService: Sheets? by derivedStateOf { - when (authenticationRepository.isAuthenticated.value) { - true -> Sheets - .Builder( - AndroidHttp.newCompatibleTransport(), - GsonFactory(), - authenticationRepository.credential, - ) - .build() - - else -> null - } - } - private val _data = MutableStateFlow>(emptyList()) val data: StateFlow> get() = _data @@ -46,156 +24,25 @@ class LexiconRepository @Inject constructor( @Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class) suspend fun fetchLexicon() { - val service = sheetService - if (service == null) { - throw ServiceNotReady() - } else { - withContext(Dispatchers.IO) { - val request = service.spreadsheets().values().get(Sheet.ID, Sheet.LEXIQUE) - val data = request.execute() - updateData(data = data) - } + googleRepository.fetch { sheet -> + val request = sheet.get(Sheet.ID, Sheet.LEXICON) + val data = request.execute() + updateData(data = data) } } @Throws(IncompatibleSheetStructure::class) - private fun updateData(data: ValueRange?) { - val sheet = data?.values?.sheet() - var sheetStructure: Map? = null - var id = 0 - val lexicon: List = sheet?.mapIndexedNotNull { index, row -> - when { - index == 0 -> { - sheetStructure = row.checkSheetStructure(model = Sheet.COLUMNS) - null - } - - row is List<*> -> parseCharacterRow( - sheetStructure = sheetStructure, - id = id, - sheetIndex = index, - row = row, - )?.also { - // update next id if parsing is successful. - id = it.id + 1 - } - - else -> null - } - } ?: emptyList() + private fun updateData(data: ValueRange) { + val lexicon = lexiconParser.parse(data) _data.tryEmit(lexicon) } - private fun parseCharacterRow( - sheetStructure: Map?, - id: Int, - sheetIndex: 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? - val tags = row?.getOrNull(sheetStructure.tags) as? String? - - return if (name != null) { - Lexicon( - id = id, - sheetIndex = sheetIndex, - name = name, - diminutive = diminutive?.takeIf { it.isNotBlank() }, - gender = when (gender?.takeIf { it.isNotBlank() }) { - Gender.MALE -> Lexicon.Gender.MALE - Gender.FEMALE -> Lexicon.Gender.FEMALE - else -> Lexicon.Gender.UNDETERMINED - }, - 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() }, - tags = tags?.takeIf { it.isNotBlank() }, - ) - } else { - null - } - } - - private val Map?.name: Int get() = this?.getValue(Sheet.NAME) ?: 0 - private val Map?.diminutive: Int get() = this?.getValue(Sheet.DIMINUTIVE) ?: 1 - private val Map?.gender: Int get() = this?.getValue(Sheet.GENDER) ?: 2 - private val Map?.race: Int get() = this?.getValue(Sheet.RACE) ?: 3 - private val Map?.portrait: Int get() = this?.getValue(Sheet.PORTRAIT) ?: 4 - private val Map?.description: Int get() = this?.getValue(Sheet.DESCRIPTION) ?: 5 - private val Map?.history: Int get() = this?.getValue(Sheet.HISTORY) ?: 6 - private val Map?.tags: Int get() = this?.getValue(Sheet.TAGS) ?: 7 - companion object { const val TAG = "LexiconRepository" } private object Sheet { const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs" - - const val LEXIQUE = "Lexique" - const val META = "MetaData" - - val COLUMNS = listOf( - "Nom", - "Diminutif", - "Sexe", - "Race", - "Portrait", - "Description", - "Histoire", - "Mots clés", - ) - - val NAME = COLUMNS[0] - val DIMINUTIVE = COLUMNS[1] - val GENDER = COLUMNS[2] - val RACE = COLUMNS[3] - val PORTRAIT = COLUMNS[4] - val DESCRIPTION = COLUMNS[5] - val HISTORY = COLUMNS[6] - val TAGS = COLUMNS[7] - } - - 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" + const val LEXICON = "Lexique" } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/LocationRepository.kt b/app/src/main/java/com/pixelized/rplexicon/repository/LocationRepository.kt new file mode 100644 index 0000000..2b77f9c --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/repository/LocationRepository.kt @@ -0,0 +1,66 @@ +package com.pixelized.rplexicon.repository + +import com.google.api.services.sheets.v4.Sheets +import com.google.api.services.sheets.v4.model.ValueRange +import com.pixelized.rplexicon.facotry.LocationParser +import com.pixelized.rplexicon.facotry.MarqueeParser +import com.pixelized.rplexicon.model.Location +import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure +import com.pixelized.rplexicon.utilitary.exceptions.ServiceNotReady +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class LocationRepository @Inject constructor( + private val googleRepository: GoogleSheetServiceRepository, + private val locationParser: LocationParser, + private val marqueeParser: MarqueeParser, +) { + private val _data = MutableStateFlow>(emptyList()) + val data: StateFlow> get() = _data + + @Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class) + suspend fun fetchLocation() { + googleRepository.fetch { sheet: Sheets.Spreadsheets.Values -> + val (map, marquee) = awaitAll( + async { sheet.get(Sheet.ID, Sheet.MAP).execute() }, + async { sheet.get(Sheet.ID, Sheet.MARQUEE).execute() }, + ) + updateData(map = map, marquee = marquee) + } + } + + @Throws(IncompatibleSheetStructure::class) + private fun updateData(map: ValueRange, marquee: ValueRange) { + val marquees = marqueeParser + .parse(data = marquee) + .groupBy { it.map } + + val maps = locationParser + .parse(data = map) + .map { + val associatedMarquees = marquees[it.name] + if (associatedMarquees != null) { + it.copy(marquees = associatedMarquees) + } else { + it + } + } + + _data.tryEmit(maps) + } + + companion object { + private const val TAG = "LocationRepository" + } + + private object Sheet { + const val ID = "1gbWaqXChz8pDJ-O3-Q8a_0wbDAYGlVA2voL7nEJUSn0" + const val MAP = "carte" + const val MARQUEE = "marqueur" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/QuestRepository.kt b/app/src/main/java/com/pixelized/rplexicon/repository/QuestRepository.kt index 9d0186a..1c325e2 100644 --- a/app/src/main/java/com/pixelized/rplexicon/repository/QuestRepository.kt +++ b/app/src/main/java/com/pixelized/rplexicon/repository/QuestRepository.kt @@ -1,82 +1,35 @@ package com.pixelized.rplexicon.repository -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import com.google.api.client.extensions.android.http.AndroidHttp -import com.google.api.client.json.gson.GsonFactory -import com.google.api.services.sheets.v4.Sheets import com.google.api.services.sheets.v4.model.ValueRange +import com.pixelized.rplexicon.facotry.QuestParser import com.pixelized.rplexicon.model.Quest -import com.pixelized.rplexicon.model.QuestEntry import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.ServiceNotReady -import com.pixelized.rplexicon.utilitary.extentions.checkSheetStructure -import com.pixelized.rplexicon.utilitary.extentions.sheet -import com.pixelized.rplexicon.utilitary.extentions.toUriOrNull -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Singleton @Singleton class QuestRepository @Inject constructor( - private val authenticationRepository: AuthenticationRepository, + private val googleRepository: GoogleSheetServiceRepository, + private val questParser: QuestParser, ) { - private val sheetService: Sheets? by derivedStateOf { - when (authenticationRepository.isAuthenticated.value) { - true -> Sheets - .Builder( - AndroidHttp.newCompatibleTransport(), - GsonFactory(), - authenticationRepository.credential, - ) - .build() - - else -> null - } - } - private val _data = MutableStateFlow>(emptyList()) val data: StateFlow> get() = _data @Throws(ServiceNotReady::class, IncompatibleSheetStructure::class, Exception::class) suspend fun fetchQuests() { - val service = sheetService - if (service == null) { - throw ServiceNotReady() - } else { - withContext(Dispatchers.IO) { - val request = service.spreadsheets().values().get(Sheet.ID, Sheet.QUEST_JOURNAL) - val data = request.execute() - updateData(data = data) - } + googleRepository.fetch { sheet -> + val request = sheet.get(Sheet.ID, Sheet.QUEST_JOURNAL) + val data = request.execute() + updateData(data = data) } } - @Throws(IncompatibleSheetStructure::class) - private fun updateData(data: ValueRange?) { - val sheet = data?.values?.sheet() - var sheetStructure: Map? = null - - val questEntries: List = sheet?.mapIndexedNotNull { index, row -> - when { - index == 0 -> { - sheetStructure = row.checkSheetStructure(model = Sheet.COLUMNS) - null - } - - row is List<*> -> parseQuestRow( - sheetStructure = sheetStructure, - sheetIndex = index, - row = row, - ) - - else -> null - } - } ?: emptyList() - + @Throws(IncompatibleSheetStructure::class, Exception::class) + private fun updateData(data: ValueRange) { + val questEntries = questParser.parse(value = data) val questMap = questEntries.groupBy { it.title } val quests = questMap.keys.mapIndexed { index, item -> @@ -86,77 +39,11 @@ class QuestRepository @Inject constructor( entries = questMap[item] ?: emptyList(), ) } - _data.tryEmit(quests) } - private fun parseQuestRow( - sheetStructure: Map?, - sheetIndex: Int, - row: List<*>?, - ): QuestEntry? { - val title = row?.getOrNull(sheetStructure.title) as? String - val subtitle = row?.getOrNull(sheetStructure.subtitle) as? String? - val complete = row?.getOrNull(sheetStructure.complete) as? String? - val questGiver = row?.getOrNull(sheetStructure.questGiver) as? String? - val area = row?.getOrNull(sheetStructure.area) as? String? - val groupReward = row?.getOrNull(sheetStructure.groupReward) as? String? - val individualReward = row?.getOrNull(sheetStructure.individualReward) as? String? - val description = row?.getOrNull(sheetStructure.description) as? String - val background = row?.getOrNull(sheetStructure.background) as? String? - - return if (title?.isNotEmpty() == true && description?.isNotEmpty() == true) { - QuestEntry( - sheetIndex = sheetIndex, - title = title, - subtitle = subtitle?.takeIf { it.isNotBlank() }, - complete = complete.equals("TRUE", ignoreCase = true), - questGiver = questGiver?.takeIf { it.isNotBlank() }, - area = area?.takeIf { it.isNotBlank() }, - groupReward = groupReward?.takeIf { it.isNotBlank() }, - individualReward = individualReward?.takeIf { it.isNotBlank() }, - description = description, - background = background?.toUriOrNull(), - ) - } else { - null - } - } - - private val Map?.title: Int get() = this?.getValue(Sheet.TITLE) ?: 0 - private val Map?.subtitle: Int get() = this?.getValue(Sheet.SUBTITLE) ?: 1 - private val Map?.complete: Int get() = this?.getValue(Sheet.COMPLETE) ?: 2 - private val Map?.questGiver: Int get() = this?.getValue(Sheet.QUEST_GIVER) ?: 3 - private val Map?.area: Int get() = this?.getValue(Sheet.AREA) ?: 4 - private val Map?.groupReward: Int get() = this?.getValue(Sheet.G_REWARD) ?: 5 - private val Map?.individualReward: Int get() = this?.getValue(Sheet.I_REWARD) ?: 6 - private val Map?.description: Int get() = this?.getValue(Sheet.DESCRIPTION) ?: 7 - private val Map?.background: Int get() = this?.getValue(Sheet.BACKGROUND) ?: 8 - private object Sheet { const val ID = "1sDAay8DjbRYKM39MvEXWs-RuvyxjOFpOfRZLAEWjIUY" - const val QUEST_JOURNAL = "Journal de quêtes" - - val COLUMNS = listOf( - "Titre", - "Sous Titre", - "Compléter", - "Commanditaire", - "Lieu", - "Récompense de groupe", - "Récompense individuelle", - "Description", - "fond" - ) - val TITLE = COLUMNS[0] - val SUBTITLE = COLUMNS[1] - val COMPLETE = COLUMNS[2] - val QUEST_GIVER = COLUMNS[3] - val AREA = COLUMNS[4] - val G_REWARD = COLUMNS[5] - val I_REWARD = COLUMNS[6] - val DESCRIPTION = COLUMNS[7] - val BACKGROUND = COLUMNS[8] } } \ No newline at end of file