Refactor the parser system to introduce a column system.

This commit is contained in:
Thomas Andres Gomez 2023-11-03 23:35:15 +01:00
parent ff1ac694bb
commit fd30b9c11e
25 changed files with 390 additions and 529 deletions

View file

@ -88,7 +88,7 @@ class AlterationRepository @Inject constructor(
suspend fun fetchAlterationSheet(sheets: List<CharacterSheet>) { suspend fun fetchAlterationSheet(sheets: List<CharacterSheet>) {
googleRepository.fetch { sheet -> googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.ALTERATION) val request = sheet.get(CharacterBinder.ID, CharacterBinder.ALTERATION)
val data = alterationParser.parse(values = request.execute(), sheets = sheets) val data = alterationParser.parse(sheet = request.execute(), characterSheets = sheets)
_assignedAlterations.emit(data) _assignedAlterations.emit(data)
lastSuccessFullUpdate = Update.currentTime() lastSuccessFullUpdate = Update.currentTime()
} }

View file

@ -28,7 +28,7 @@ class DescriptionRepository @Inject constructor(
suspend fun fetchDescription() { suspend fun fetchDescription() {
googleRepository.fetch { sheet -> googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.DESCRIPTION) val request = sheet.get(CharacterBinder.ID, CharacterBinder.DESCRIPTION)
val data = parser.parse(data = request.execute()) val data = parser.parse(sheet = request.execute())
_data.tryEmit(data) _data.tryEmit(data)
lastSuccessFullUpdate = Update.currentTime() lastSuccessFullUpdate = Update.currentTime()

View file

@ -28,7 +28,7 @@ class InventoryRepository @Inject constructor(
suspend fun fetchInventory() { suspend fun fetchInventory() {
googleRepository.fetch { sheet -> googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.INVENTORY) val request = sheet.get(CharacterBinder.ID, CharacterBinder.INVENTORY)
val data = inventoryParser.parse(data = request.execute()) val data = inventoryParser.parse(sheet = request.execute())
_data.tryEmit(data) _data.tryEmit(data)
lastSuccessFullUpdate = Update.currentTime() lastSuccessFullUpdate = Update.currentTime()
} }

View file

@ -30,7 +30,7 @@ class SkillRepository @Inject constructor(
suspend fun fetchSkills() { suspend fun fetchSkills() {
googleRepository.fetch { sheet -> googleRepository.fetch { sheet ->
val request = sheet.get(CharacterBinder.ID, CharacterBinder.SKILL) val request = sheet.get(CharacterBinder.ID, CharacterBinder.SKILL)
val skills = skillParser.parse(data = request.execute()) val skills = skillParser.parse(sheet = request.execute())
_skills.emit(skills) _skills.emit(skills)
lastSuccessFullUpdate = Update.currentTime() lastSuccessFullUpdate = Update.currentTime()

View file

@ -43,8 +43,8 @@ class SpellRepository @Inject constructor(
async { sheet.get(CharacterBinder.ID, CharacterBinder.MAGIC_LEXICON).execute() }, async { sheet.get(CharacterBinder.ID, CharacterBinder.MAGIC_LEXICON).execute() },
async { sheet.get(CharacterBinder.ID, CharacterBinder.MAGIC).execute() }, async { sheet.get(CharacterBinder.ID, CharacterBinder.MAGIC).execute() },
) )
val spellsBook = spellBookParser.parse(data = lexicon) val spellsBook = spellBookParser.parse(sheet = lexicon)
val assignedSpells = assignedSpellParser.parse(data = magic, spells = spellsBook) val assignedSpells = assignedSpellParser.parse(sheet = magic, spells = spellsBook)
this@SpellRepository.spellsBook = spellsBook this@SpellRepository.spellsBook = spellsBook
_spells.emit(assignedSpells) _spells.emit(assignedSpells)
lastSuccessFullUpdate = Update.currentTime() lastSuccessFullUpdate = Update.currentTime()

View file

@ -30,7 +30,7 @@ class LexiconRepository @Inject constructor(
suspend fun fetchLexicon() { suspend fun fetchLexicon() {
googleRepository.fetch { sheet -> googleRepository.fetch { sheet ->
val request = sheet.get(LexiconBinder.ID, LexiconBinder.LEXICON) val request = sheet.get(LexiconBinder.ID, LexiconBinder.LEXICON)
val data = lexiconParser.parse(data = request.execute()) val data = lexiconParser.parse(sheet = request.execute())
_data.tryEmit(data) _data.tryEmit(data)
lastSuccessFullUpdate = Update.currentTime() lastSuccessFullUpdate = Update.currentTime()

View file

@ -46,11 +46,11 @@ class LocationRepository @Inject constructor(
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
private suspend fun updateData(map: ValueRange, marquee: ValueRange) { private suspend fun updateData(map: ValueRange, marquee: ValueRange) {
val marquees = marqueeParser val marquees = marqueeParser
.parse(data = marquee) .parse(sheet = marquee)
.groupBy { it.map } .groupBy { it.map }
val maps = locationParser val maps = locationParser
.parse(data = map) .parse(sheet = map)
.map { .map {
val associatedMarquees = marquees[it.name] val associatedMarquees = marquees[it.name]
if (associatedMarquees != null) { if (associatedMarquees != null) {

View file

@ -35,7 +35,7 @@ class QuestRepository @Inject constructor(
@Throws(IncompatibleSheetStructure::class, Exception::class) @Throws(IncompatibleSheetStructure::class, Exception::class)
private suspend fun updateData(data: ValueRange) { private suspend fun updateData(data: ValueRange) {
val questEntries = questParser.parse(value = data) val questEntries = questParser.parse(sheet = data)
val questMap = questEntries.groupBy { it.title } val questMap = questEntries.groupBy { it.title }
val quests = questMap.keys.mapIndexed { index, item -> val quests = questMap.keys.mapIndexed { index, item ->
Quest( Quest(

View file

@ -5,8 +5,6 @@ import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Attack import com.pixelized.rplexicon.model.Attack
import com.pixelized.rplexicon.model.CharacterSheet import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
class AttackParser @Inject constructor( class AttackParser @Inject constructor(
@ -16,34 +14,24 @@ class AttackParser @Inject constructor(
fun parse( fun parse(
value: ValueRange, value: ValueRange,
charactersSheets: Map<String, CharacterSheet>, charactersSheets: Map<String, CharacterSheet>,
): Map<String, List<Attack>> { ): Map<String, List<Attack>> = parserScope {
val sheet = value.values.sheet()
lateinit var structure: Map<String, Int>
// declare helper method to parse String // declare helper method to parse String
fun List<*>.parseString(key: String): String? =
(getOrNull(structure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() }
val actions = hashMapOf<String, MutableList<Attack>>() val actions = hashMapOf<String, MutableList<Attack>>()
sheet?.forEachIndexed { index, row -> value.forEachRow { index, row ->
when { when (index) {
index == 0 -> { 0 -> updateStructure(row = row, columns = COLUMNS)
structure = row.checkSheetStructure(model = COLUMNS) else -> {
}
row is List<*> -> {
// Assume that the name is the first column. // Assume that the name is the first column.
val characterSheet = charactersSheets[row.getOrNull(0) as? String ?: ""] val characterSheet = charactersSheets[row.parse(CHARACTER)]
val title = row.parseString(NAME) val title = row.parse(column = NAME)
if (characterSheet != null && title != null) { if (characterSheet != null && title != null) {
val attack = Attack( val attack = Attack(
title = title, title = title,
type = parseType(value = row.parseString(TYPE)), type = parseType(value = row.parse(column = TYPE)),
range = row.parseString(RANGE), range = row.parse(column = RANGE),
hit = throwParser.parse(value = row.parseString(HIT)), hit = throwParser.parse(value = row.parse(column = HIT)),
damage = throwParser.parse(value = row.parseString(DAMAGE)), damage = throwParser.parse(value = row.parse(column = DAMAGE)),
) )
actions actions
.getOrPut(characterSheet.name) { mutableListOf() } .getOrPut(characterSheet.name) { mutableListOf() }
@ -53,7 +41,7 @@ class AttackParser @Inject constructor(
} }
} }
return actions actions
} }
private fun parseType(value: String?): Attack.Type = try { private fun parseType(value: String?): Attack.Type = try {
@ -64,12 +52,12 @@ class AttackParser @Inject constructor(
} ?: Attack.Type.PHYSICAL_MELEE_ATTACK } ?: Attack.Type.PHYSICAL_MELEE_ATTACK
companion object { companion object {
const val NAME = "Nom" private val CHARACTER = column("")
const val TYPE = "Type" private val NAME = column("Nom")
const val RANGE = "Portée" private val TYPE = column("Type")
const val HIT = "Touché" private val RANGE = column("Portée")
const val DAMAGE = "Dommage" private val HIT = column("Touché")
private val DAMAGE = column("Dommage")
val COLUMNS get() = listOf("", NAME, TYPE, RANGE, HIT, DAMAGE) private val COLUMNS get() = listOf(CHARACTER, NAME, TYPE, RANGE, HIT, DAMAGE)
} }
} }

View file

@ -3,7 +3,6 @@ package com.pixelized.rplexicon.repository.parser
import com.google.api.services.sheets.v4.model.ValueRange import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.CharacterSheet import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
@ -11,8 +10,7 @@ import javax.inject.Inject
class CharacterSheetParser @Inject constructor() { class CharacterSheetParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse(value: ValueRange): Map<String, CharacterSheet> { fun parse(value: ValueRange): Map<String, CharacterSheet> = parserScope {
// fetch the character sheet // fetch the character sheet
val sheet = value.values.sheet() // fetch the List<*> val sheet = value.values.sheet() // fetch the List<*>
?.mapNotNull { it as? List<*> } // transform the List<*> to a List<List<*>> ?.mapNotNull { it as? List<*> } // transform the List<*> to a List<List<*>>
@ -20,79 +18,66 @@ class CharacterSheetParser @Inject constructor() {
val entryCount = if (sheet.isNotEmpty()) sheet[0].size else 0 val entryCount = if (sheet.isNotEmpty()) sheet[0].size else 0
List(entryCount) { index -> sheet.map { it.getOrNull(index) } } List(entryCount) { index -> sheet.map { it.getOrNull(index) } }
} }
// prepare a hash on the structure & function to get index in it.
lateinit var structure: Map<String, Int>
// declare helper method to parse String
fun List<*>.parseString(key: String): String? =
(getOrNull(structure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() }
// declare helper method to parse Int
fun List<*>.parseInt(key: String): Int? = parseString(key = key)?.toIntOrNull()
// parse the sheet // parse the sheet
return sheet?.mapIndexedNotNull { index, item -> sheet?.mapIndexedNotNull { index, item ->
when (index) { when (index) {
0 -> { 0 -> {
structure = item.checkSheetStructure(model = ROWS) updateStructure(row = item, columns = ROWS)
return@mapIndexedNotNull null null
} }
else -> { else -> {
val name = item.parseString(NAME) val name = item.parse(column = NAME)
if (name != null) { if (name != null) {
CharacterSheet( CharacterSheet(
name = name, name = name,
race = item.parseString(RACE), race = item.parse(column = RACE),
proficiency = item.parseInt(MASTERY) ?: 2, proficiency = item.parseInt(column = MASTERY) ?: 2,
level = item.parseInt(LEVEL) ?: 2, level = item.parseInt(column = LEVEL) ?: 2,
characterClass = item.parseString(CLASS) ?: "", characterClass = item.parse(column = CLASS) ?: "",
hitPoint = item.parseInt(MAX_HIT_POINT) ?: 1, hitPoint = item.parseInt(column = MAX_HIT_POINT) ?: 1,
spell1 = item.parseInt(SPELL_LEVEL_1), spell1 = item.parseInt(column = SPELL_LEVEL_1),
spell2 = item.parseInt(SPELL_LEVEL_2), spell2 = item.parseInt(column = SPELL_LEVEL_2),
spell3 = item.parseInt(SPELL_LEVEL_3), spell3 = item.parseInt(column = SPELL_LEVEL_3),
spell4 = item.parseInt(SPELL_LEVEL_4), spell4 = item.parseInt(column = SPELL_LEVEL_4),
spell5 = item.parseInt(SPELL_LEVEL_5), spell5 = item.parseInt(column = SPELL_LEVEL_5),
spell6 = item.parseInt(SPELL_LEVEL_6), spell6 = item.parseInt(column = SPELL_LEVEL_6),
spell7 = item.parseInt(SPELL_LEVEL_7), spell7 = item.parseInt(column = SPELL_LEVEL_7),
spell8 = item.parseInt(SPELL_LEVEL_8), spell8 = item.parseInt(column = SPELL_LEVEL_8),
spell9 = item.parseInt(SPELL_LEVEL_9), spell9 = item.parseInt(column = SPELL_LEVEL_9),
dC = item.parseInt(DD_SAVE_THROW), dC = item.parseInt(column = DD_SAVE_THROW),
armorClass = item.parseInt(ARMOR_CLASS) ?: 10, armorClass = item.parseInt(column = ARMOR_CLASS) ?: 10,
speed = item.parseInt(SPEED) ?: 10, speed = item.parseInt(column = SPEED) ?: 10,
strength = item.parseInt(STRENGTH) ?: 10, strength = item.parseInt(column = STRENGTH) ?: 10,
dexterity = item.parseInt(DEXTERITY) ?: 10, dexterity = item.parseInt(column = DEXTERITY) ?: 10,
constitution = item.parseInt(CONSTITUTION) ?: 10, constitution = item.parseInt(column = CONSTITUTION) ?: 10,
intelligence = item.parseInt(INTELLIGENCE) ?: 10, intelligence = item.parseInt(column = INTELLIGENCE) ?: 10,
wisdom = item.parseInt(WISDOM) ?: 10, wisdom = item.parseInt(column = WISDOM) ?: 10,
charisma = item.parseInt(CHARISMA) ?: 10, charisma = item.parseInt(column = CHARISMA) ?: 10,
strengthSavingThrows = item.parseInt(STRENGTH_SAVING_THROW) ?: 0, strengthSavingThrows = item.parseInt(column = STRENGTH_SAVING_THROW) ?: 0,
dexteritySavingThrows = item.parseInt(DEXTERITY_SAVING_THROW) ?: 0, dexteritySavingThrows = item.parseInt(column = DEXTERITY_SAVING_THROW) ?: 0,
constitutionSavingThrows = item.parseInt(CONSTITUTION_SAVING_THROW) constitutionSavingThrows = item.parseInt(column = CONSTITUTION_SAVING_THROW) ?: 0,
?: 0, intelligenceSavingThrows = item.parseInt(column = INTELLIGENCE_SAVING_THROW) ?: 0,
intelligenceSavingThrows = item.parseInt(INTELLIGENCE_SAVING_THROW) wisdomSavingThrows = item.parseInt(column = WISDOM_SAVING_THROW) ?: 0,
?: 0, charismaSavingThrows = item.parseInt(column = CHARISMA_SAVING_THROW) ?: 0,
wisdomSavingThrows = item.parseInt(WISDOM_SAVING_THROW) ?: 0, acrobatics = item.parseInt(column = ACROBATICS) ?: 0,
charismaSavingThrows = item.parseInt(CHARISMA_SAVING_THROW) ?: 0, animalHandling = item.parseInt(column = ANIMAL_HANDLING) ?: 0,
acrobatics = item.parseInt(ACROBATICS) ?: 0, arcana = item.parseInt(column = ARCANA) ?: 0,
animalHandling = item.parseInt(ANIMAL_HANDLING) ?: 0, athletics = item.parseInt(column = ATHLETICS) ?: 0,
arcana = item.parseInt(ARCANA) ?: 0, deception = item.parseInt(column = DECEPTION) ?: 0,
athletics = item.parseInt(ATHLETICS) ?: 0, history = item.parseInt(column = HISTORY) ?: 0,
deception = item.parseInt(DECEPTION) ?: 0, insight = item.parseInt(column = INSIGHT) ?: 0,
history = item.parseInt(HISTORY) ?: 0, intimidation = item.parseInt(column = INTIMIDATION) ?: 0,
insight = item.parseInt(INSIGHT) ?: 0, investigation = item.parseInt(column = INVESTIGATION) ?: 0,
intimidation = item.parseInt(INTIMIDATION) ?: 0, medicine = item.parseInt(column = MEDICINE) ?: 0,
investigation = item.parseInt(INVESTIGATION) ?: 0, nature = item.parseInt(column = NATURE) ?: 0,
medicine = item.parseInt(MEDICINE) ?: 0, perception = item.parseInt(column = PERCEPTION) ?: 0,
nature = item.parseInt(NATURE) ?: 0, performance = item.parseInt(column = PERFORMANCE) ?: 0,
perception = item.parseInt(PERCEPTION) ?: 0, persuasion = item.parseInt(column = PERSUASION) ?: 0,
performance = item.parseInt(PERFORMANCE) ?: 0, religion = item.parseInt(column = RELIGION) ?: 0,
persuasion = item.parseInt(PERSUASION) ?: 0, sleightOfHand = item.parseInt(column = SLEIGHT_OF_HAND) ?: 0,
religion = item.parseInt(RELIGION) ?: 0, stealth = item.parseInt(column = STEALTH) ?: 0,
sleightOfHand = item.parseInt(SLEIGHT_OF_HAND) ?: 0, survival = item.parseInt(column = SURVIVAL) ?: 0,
stealth = item.parseInt(STEALTH) ?: 0,
survival = item.parseInt(SURVIVAL) ?: 0,
) )
} else { } else {
null null
@ -103,54 +88,54 @@ class CharacterSheetParser @Inject constructor() {
} }
companion object { companion object {
private const val NAME = "Nom" private val NAME = column("Nom")
private const val RACE = "Race" private val RACE = column("Race")
private const val LEVEL = "Niveau" private val LEVEL = column("Niveau")
private const val CLASS = "Classe" private val CLASS = column("Classe")
private const val MAX_HIT_POINT = "Point de vie MAX" private val MAX_HIT_POINT = column("Point de vie MAX")
private const val SPELL_LEVEL_1 = "Sort de niveau 1" private val SPELL_LEVEL_1 = column("Sort de niveau 1")
private const val SPELL_LEVEL_2 = "Sort de niveau 2" private val SPELL_LEVEL_2 = column("Sort de niveau 2")
private const val SPELL_LEVEL_3 = "Sort de niveau 3" private val SPELL_LEVEL_3 = column("Sort de niveau 3")
private const val SPELL_LEVEL_4 = "Sort de niveau 4" private val SPELL_LEVEL_4 = column("Sort de niveau 4")
private const val SPELL_LEVEL_5 = "Sort de niveau 5" private val SPELL_LEVEL_5 = column("Sort de niveau 5")
private const val SPELL_LEVEL_6 = "Sort de niveau 6" private val SPELL_LEVEL_6 = column("Sort de niveau 6")
private const val SPELL_LEVEL_7 = "Sort de niveau 7" private val SPELL_LEVEL_7 = column("Sort de niveau 7")
private const val SPELL_LEVEL_8 = "Sort de niveau 8" private val SPELL_LEVEL_8 = column("Sort de niveau 8")
private const val SPELL_LEVEL_9 = "Sort de niveau 9" private val SPELL_LEVEL_9 = column("Sort de niveau 9")
private const val DD_SAVE_THROW = "DD sauvergarde des sorts" private val DD_SAVE_THROW = column("DD sauvergarde des sorts")
private const val ARMOR_CLASS = "Classe d'armure" private val ARMOR_CLASS = column("Classe d'armure")
private const val SPEED = "Vitesse" private val SPEED = column("Vitesse")
private const val MASTERY = "Bonus de maîtrise" private val MASTERY = column("Bonus de maîtrise")
private const val STRENGTH = "Force" private val STRENGTH = column("Force")
private const val DEXTERITY = "Dextérité" private val DEXTERITY = column("Dextérité")
private const val CONSTITUTION = "Constitution" private val CONSTITUTION = column("Constitution")
private const val INTELLIGENCE = "Intelligence" private val INTELLIGENCE = column("Intelligence")
private const val WISDOM = "Sagesse" private val WISDOM = column("Sagesse")
private const val CHARISMA = "Charisme" private val CHARISMA = column("Charisme")
private const val STRENGTH_SAVING_THROW = "Jet de sauvegarde: Force" private val STRENGTH_SAVING_THROW = column("Jet de sauvegarde: Force")
private const val DEXTERITY_SAVING_THROW = "Jet de sauvegarde: Dextérité" private val DEXTERITY_SAVING_THROW = column("Jet de sauvegarde: Dextérité")
private const val CONSTITUTION_SAVING_THROW = "Jet de sauvegarde: Constitution" private val CONSTITUTION_SAVING_THROW = column("Jet de sauvegarde: Constitution")
private const val INTELLIGENCE_SAVING_THROW = "Jet de sauvegarde: Intelligence" private val INTELLIGENCE_SAVING_THROW = column("Jet de sauvegarde: Intelligence")
private const val WISDOM_SAVING_THROW = "Jet de sauvegarde: Sagesse" private val WISDOM_SAVING_THROW = column("Jet de sauvegarde: Sagesse")
private const val CHARISMA_SAVING_THROW = "Jet de sauvegarde: Charisme" private val CHARISMA_SAVING_THROW = column("Jet de sauvegarde: Charisme")
private const val ACROBATICS = "Acrobaties" private val ACROBATICS = column("Acrobaties")
private const val ANIMAL_HANDLING = "Dressage" private val ANIMAL_HANDLING = column("Dressage")
private const val ARCANA = "Arcanes" private val ARCANA = column("Arcanes")
private const val ATHLETICS = "Athlétisme" private val ATHLETICS = column("Athlétisme")
private const val DECEPTION = "Tromperie" private val DECEPTION = column("Tromperie")
private const val HISTORY = "Histoire" private val HISTORY = column("Histoire")
private const val INSIGHT = "Intuition" private val INSIGHT = column("Intuition")
private const val INTIMIDATION = "Intimidation" private val INTIMIDATION = column("Intimidation")
private const val INVESTIGATION = "Investigation" private val INVESTIGATION = column("Investigation")
private const val MEDICINE = "Médecine" private val MEDICINE = column("Médecine")
private const val NATURE = "Nature" private val NATURE = column("Nature")
private const val PERCEPTION = "Perception" private val PERCEPTION = column("Perception")
private const val PERFORMANCE = "Représentation" private val PERFORMANCE = column("Représentation")
private const val PERSUASION = "Persuasion" private val PERSUASION = column("Persuasion")
private const val RELIGION = "Religion" private val RELIGION = column("Religion")
private const val SLEIGHT_OF_HAND = "Escamotage" private val SLEIGHT_OF_HAND = column("Escamotage")
private const val STEALTH = "Discrétion" private val STEALTH = column("Discrétion")
private const val SURVIVAL = "Survie" private val SURVIVAL = column("Survie")
private val ROWS private val ROWS
get() = listOf( get() = listOf(

View file

@ -0,0 +1,11 @@
package com.pixelized.rplexicon.repository.parser
data class Column(
val names: List<String>,
) {
override fun toString(): String {
return names.toString()
}
}
fun column(vararg names: String) = Column(names = names.toList())

View file

@ -3,32 +3,23 @@ package com.pixelized.rplexicon.repository.parser
import com.google.api.services.sheets.v4.model.ValueRange import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Description import com.pixelized.rplexicon.model.Description
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
class DescriptionParser @Inject constructor() { class DescriptionParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): Map<String, Description> { fun parse(sheet: ValueRange): Map<String, Description> = parserScope {
val sheet = data.values.sheet() val descriptions = hashMapOf<String, Description>()
val values = hashMapOf<String, Description>()
lateinit var structure: Map<String, Int>
sheet?.forEachIndexed { index, item ->
when {
index == 0 -> {
structure = item.checkSheetStructure(model = COLUMNS)
}
item is List<*> -> {
val name = item.getOrNull(structure.getValue(NAME)) as? String
val translate = item.getOrNull(structure.getValue(TRANSLATE)) as? String
val description = item.getOrNull(structure.getValue(DESCRIPTION)) as? String
sheet.forEachRow { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {
val name = row.parse(column = NAME)
val translate = row.parse(column = TRANSLATE)
val description = row.parse(column = DESCRIPTION)
if (name != null && translate != null && description != null) { if (name != null && translate != null && description != null) {
values[name] = Description( descriptions[name] = Description(
name = name, name = name,
original = translate, original = translate,
description = description, description = description,
@ -38,13 +29,13 @@ class DescriptionParser @Inject constructor() {
} }
} }
return values return@parserScope descriptions
} }
companion object { companion object {
private const val NAME = "Nom" private val NAME = column("Nom")
private const val TRANSLATE = "Traduction" private val TRANSLATE = column("Traduction")
private const val DESCRIPTION = "Description" private val DESCRIPTION = column("Description")
private val COLUMNS = listOf( private val COLUMNS = listOf(
NAME, NAME,

View file

@ -3,8 +3,6 @@ package com.pixelized.rplexicon.repository.parser
import com.google.api.services.sheets.v4.model.ValueRange import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Lexicon import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
class LexiconParser @Inject constructor( class LexiconParser @Inject constructor(
@ -12,80 +10,65 @@ class LexiconParser @Inject constructor(
private val genderParser: GenderParser, private val genderParser: GenderParser,
private val raceParser: RaceParser, private val raceParser: RaceParser,
) { ) {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): List<Lexicon> { fun parse(sheet: ValueRange): List<Lexicon> = parserScope {
val sheet = data.values.sheet()
lateinit var sheetStructure: Map<String, Int>
var id = 0 var id = 0
val lexicons = mutableListOf<Lexicon>()
return sheet?.mapIndexedNotNull { index, row -> sheet.forEachRow { index, row ->
when { when (index) {
index == 0 -> { 0 -> updateStructure(row = row, columns = COLUMNS)
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 status = row.getOrNull(sheetStructure.status) as? String
val location = row.getOrNull(sheetStructure.location) 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
else -> {
val name = row.parse(column = NAME)
if (name != null) { if (name != null) {
Lexicon( val lexicon = Lexicon(
id = id++, id = id++,
sheetIndex = index, sheetIndex = index,
name = name, name = name,
diminutive = diminutive?.takeIf { it.isNotBlank() }, diminutive = row.parse(column = SHORT),
gender = genderParser.parse(gender), gender = genderParser.parse(row.parse(column = GENDER)),
race = raceParser.parser(race), race = raceParser.parser(row.parse(column = RACE)),
portrait = portraitParser.parse(portrait), status = row.parse(column = STATUS),
status = status?.takeIf { it.isNotBlank() }, location = row.parse(column = LOCATION),
location = location?.takeIf { it.isNotBlank() }, portrait = portraitParser.parse(row.parse(column = PORTRAIT)),
description = description?.takeIf { it.isNotBlank() }, description = row.parse(column = DESCRIPTION),
history = history?.takeIf { it.isNotBlank() }, history = row.parse(column = HISTORY),
tags = tags?.takeIf { it.isNotBlank() }, tags = row.parse(column = TAGS),
) )
} else { lexicons.add(lexicon)
null
} }
} }
else -> null
} }
} ?: emptyList() }
}
private val Map<String, Int>.name: Int get() = getValue(COLUMNS[0]) return@parserScope lexicons
private val Map<String, Int>.diminutive: Int get() = getValue(COLUMNS[1]) }
private val Map<String, Int>.gender: Int get() = getValue(COLUMNS[2])
private val Map<String, Int>.race: Int get() = getValue(COLUMNS[3])
private val Map<String, Int>.status: Int get() = getValue(COLUMNS[4])
private val Map<String, Int>.location: Int get() = getValue(COLUMNS[5])
private val Map<String, Int>.portrait: Int get() = getValue(COLUMNS[6])
private val Map<String, Int>.description: Int get() = getValue(COLUMNS[7])
private val Map<String, Int>.history: Int get() = getValue(COLUMNS[8])
private val Map<String, Int>.tags: Int get() = getValue(COLUMNS[9])
companion object { companion object {
private val COLUMNS = listOf( private val NAME = column("Nom")
"Nom", private val SHORT = column("Diminutif")
"Diminutif", private val GENDER = column("Sexe")
"Sexe", private val RACE = column("Race")
"Race", private val STATUS = column("Statut")
"Statut", private val LOCATION = column("Localisation")
"Localisation", private val PORTRAIT = column("Portrait")
"Portrait", private val DESCRIPTION = column("Description")
"Description", private val HISTORY = column("Histoire")
"Histoire", private val TAGS = column("Mots clés")
"Mots clés",
) private val COLUMNS
get() = listOf(
NAME,
SHORT,
GENDER,
RACE,
STATUS,
LOCATION,
PORTRAIT,
DESCRIPTION,
HISTORY,
TAGS,
)
} }
} }

View file

@ -1,55 +1,43 @@
package com.pixelized.rplexicon.repository.parser package com.pixelized.rplexicon.repository.parser
import android.net.Uri
import com.google.api.services.sheets.v4.model.ValueRange import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Location import com.pixelized.rplexicon.model.Location
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
class LocationParser @Inject constructor() { class LocationParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): List<Location> { fun parse(sheet: ValueRange): List<Location> = parserScope {
val locations = mutableListOf<Location>()
var id = 0 var id = 0
var sheetStructure: Map<String, Int>? = 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) }
sheet.forEachRow { index, row ->
when (index) {
0 -> updateStructure(row = row, columns = COLUMNS)
else -> {
val name = row.parse(column = NAME)
val uri = row.parseUri(column = MAP)
if (name != null && uri != null) { if (name != null && uri != null) {
Location( val location = Location(
id = id++, id = id++,
sheetIndex = index, sheetIndex = index,
name = name, name = name,
uri = uri, uri = uri,
marquees = emptyList(), marquees = emptyList(),
) )
} else { locations.add(location)
null
} }
} }
else -> null
} }
} ?: emptyList() }
}
private val Map<String, Int>?.name: Int get() = this?.getValue(COLUMNS[0]) ?: 0 return@parserScope locations
private val Map<String, Int>?.uri: Int get() = this?.getValue(COLUMNS[1]) ?: 1 }
companion object { companion object {
private val COLUMNS = listOf( private val NAME = column("Nom")
"Nom", "Carte" private val MAP = column("Carte")
) private val COLUMNS get() = listOf(NAME, MAP)
} }
} }

View file

@ -3,63 +3,52 @@ package com.pixelized.rplexicon.repository.parser
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import com.google.api.services.sheets.v4.model.ValueRange import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Location import com.pixelized.rplexicon.model.Location
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
class MarqueeParser @Inject constructor() { class MarqueeParser @Inject constructor() {
fun parse(data: ValueRange): List<Location.Marquee> { @Throws(IncompatibleSheetStructure::class)
val sheet = data.values.sheet() fun parse(sheet: ValueRange): List<Location.Marquee> = parserScope {
lateinit var structure: Map<String, Int> val marquees = mutableListOf<Location.Marquee>()
return sheet?.mapIndexedNotNull { index, item -> sheet.forEachRow { index, item ->
when { when (index) {
index == 0 -> { 0 -> updateStructure(row = item, columns = COLUMNS)
structure = item.checkSheetStructure(model = COLUMNS)
null
}
item is List<*> -> { else -> {
val map = item.getOrNull(structure.map) as? String val map = item.parse(column = MAP)
val name = item.getOrNull(structure.name) as? String? val x = item.parse(column = X)
val x = (item.getOrNull(structure.x) as? String)
?.replace(oldValue = ",", newValue = ".") ?.replace(oldValue = ",", newValue = ".")
?.toFloatOrNull() ?.toFloatOrNull()
val y = (item.getOrNull(structure.y) as? String) val y = item.parse(column = Y)
?.replace(oldValue = ",", newValue = ".") ?.replace(oldValue = ",", newValue = ".")
?.toFloatOrNull() ?.toFloatOrNull()
val description = item.getOrNull(structure.description) as? String?
if (map != null) { if (map != null) {
Location.Marquee( val marquee = Location.Marquee(
map = map, map = map,
name = name?.takeIf { it.isNotBlank() }, name = item.parse(column = NAME),
position = when { position = when {
x != null && y != null -> Offset(x, y) x != null && y != null -> Offset(x, y)
else -> Offset.Unspecified else -> Offset.Unspecified
}, },
description = description?.takeIf { it.isNotBlank() }, description = item.parse(column = DESCRIPTION),
) )
} else { marquees.add(marquee)
null
} }
} }
else -> null
} }
} ?: emptyList() }
}
private val Map<String, Int>.map: Int get() = getValue(COLUMNS[0]) return@parserScope marquees
private val Map<String, Int>.name: Int get() = getValue(COLUMNS[1]) }
private val Map<String, Int>.x: Int get() = getValue(COLUMNS[2])
private val Map<String, Int>.y: Int get() = getValue(COLUMNS[3])
private val Map<String, Int>.description: Int get() = getValue(COLUMNS[4])
companion object { companion object {
private val COLUMNS = listOf( private val MAP = column("Carte")
"Carte", "Nom", "X", "Y", "Description" private val NAME = column("Nom")
) private val X = column("X")
private val Y = column("Y")
private val DESCRIPTION = column("Description")
private val COLUMNS get() = listOf(MAP, NAME, X, Y, DESCRIPTION)
} }
} }

View file

@ -36,8 +36,8 @@ class ObjectActionParser @Inject constructor(
} }
companion object { companion object {
private const val NAME = "Nom" private val NAME = column("Nom")
private const val EFFECT = "Effet" private val EFFECT = column("Effet")
private val COLUMNS = listOf(NAME, EFFECT) private val COLUMNS = listOf(NAME, EFFECT)
} }
} }

View file

@ -8,28 +8,28 @@ import javax.inject.Inject
class QuestParser @Inject constructor( class QuestParser @Inject constructor(
private val imageParser: PortraitParser private val imageParser: PortraitParser
) { ) {
fun parse(value: ValueRange): List<QuestEntry> = parserScope { fun parse(sheet: ValueRange): List<QuestEntry> = parserScope {
val quest = mutableListOf<QuestEntry>() val quest = mutableListOf<QuestEntry>()
value.forEachRow { index, item -> sheet.forEachRow { index, item ->
when (index) { when (index) {
0 -> updateStructure(row = item, columns = COLUMNS) 0 -> updateStructure(row = item, columns = COLUMNS)
else -> { else -> {
val title = item.parse(TITLE) val title = item.parse(column = TITLE)
val description = item.parse(DESCRIPTION) val description = item.parse(column = DESCRIPTION)
if (title?.isNotEmpty() == true && description?.isNotEmpty() == true) { if (title?.isNotEmpty() == true && description?.isNotEmpty() == true) {
val entry = QuestEntry( val entry = QuestEntry(
sheetIndex = index, sheetIndex = index,
title = title, title = title,
subtitle = item.parse(SUB_TITLE), subtitle = item.parse(column = SUB_TITLE),
complete = item.parseBool(COMPLETED) ?: false, complete = item.parseBool(column = COMPLETED) ?: false,
questGiver = item.parse(QUEST_GIVER), questGiver = item.parse(column = QUEST_GIVER),
area = item.parse(AREA), area = item.parse(column = AREA),
groupReward = item.parse(GROUP_REWARD), groupReward = item.parse(column = GROUP_REWARD),
individualReward = item.parse(INDIVIDUAL_REWARD), individualReward = item.parse(column = INDIVIDUAL_REWARD),
description = description, description = description,
images = imageParser.parse(item.parse(IMAGE)), images = imageParser.parse(item.parse(column = IMAGE)),
background = item.parseUri(BACKGROUND), background = item.parseUri(column = BACKGROUND),
) )
quest.add(entry) quest.add(entry)
} }
@ -37,32 +37,33 @@ class QuestParser @Inject constructor(
} }
} }
quest return@parserScope quest
} }
companion object { companion object {
private const val TITLE = "Titre" private val TITLE = column("Titre")
private const val SUB_TITLE = "Sous Titre" private val SUB_TITLE = column("Sous Titre")
private const val COMPLETED = "Compléter" private val COMPLETED = column("Compléter")
private const val QUEST_GIVER = "Commanditaire" private val QUEST_GIVER = column("Commanditaire")
private const val AREA = "Lieu" private val AREA = column("Lieu")
private const val GROUP_REWARD = "Récompense de groupe" private val GROUP_REWARD = column("Récompense de groupe")
private const val INDIVIDUAL_REWARD = "Récompense individuelle" private val INDIVIDUAL_REWARD = column("Récompense individuelle")
private const val DESCRIPTION = "Description" private val DESCRIPTION = column("Description")
private const val IMAGE = "Image" private val IMAGE = column("Image")
private const val BACKGROUND = "fond" // TODO private val BACKGROUND = column("fond", "Fond") // TODO remove "fond" after 0.7.0 release
private val COLUMNS = listOf( private val COLUMNS
TITLE, get() = listOf(
SUB_TITLE, TITLE,
COMPLETED, SUB_TITLE,
QUEST_GIVER, COMPLETED,
AREA, QUEST_GIVER,
GROUP_REWARD, AREA,
INDIVIDUAL_REWARD, GROUP_REWARD,
DESCRIPTION, INDIVIDUAL_REWARD,
IMAGE, DESCRIPTION,
BACKGROUND, IMAGE,
) BACKGROUND,
)
} }
} }

View file

@ -13,9 +13,9 @@ inline fun <reified T> parserScope(
} }
class SheetParserScope<T> { class SheetParserScope<T> {
private var structure: Map<String, Int> = hashMapOf() private var structure: Map<Column, Int> = hashMapOf()
fun updateStructure(row: Any, columns: List<String>) { fun updateStructure(row: Any, columns: List<Column>) {
structure = row.checkSheetStructure(model = columns) structure = row.checkSheetStructure(model = columns)
} }
@ -31,15 +31,15 @@ class SheetParserScope<T> {
fun Any?.toItem(): String? = fun Any?.toItem(): String? =
this.toString().takeIf { it.isNotBlank() } this.toString().takeIf { it.isNotBlank() }
fun List<*>.parse(column: String): String? = fun List<*>.parse(column: Column): String? =
getOrNull(structure.getValue(column))?.toItem() getOrNull(structure.getValue(column))?.toItem()
fun List<*>.parseInt(column: String): Int? = fun List<*>.parseInt(column: Column): Int? =
parse(column)?.toIntOrNull() parse(column)?.toIntOrNull()
fun List<*>.parseBool(column: String): Boolean? = fun List<*>.parseBool(column: Column): Boolean? =
parse(column)?.equals("TRUE", ignoreCase = true) parse(column)?.equals("TRUE", ignoreCase = true)
fun List<*>.parseUri(column: String): Uri? = fun List<*>.parseUri(column: Column): Uri? =
parse(column)?.takeIf { it.isNotBlank() }?.toUri() parse(column)?.takeIf { it.isNotBlank() }?.toUri()
} }

View file

@ -3,30 +3,19 @@ package com.pixelized.rplexicon.repository.parser
import com.google.api.services.sheets.v4.model.ValueRange import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Skill import com.pixelized.rplexicon.model.Skill
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
class SkillParser @Inject constructor( class SkillParser @Inject constructor(
private val throwParser: ThrowParser, private val throwParser: ThrowParser,
) { ) {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): Map<String, List<Skill>> { fun parse(sheet: ValueRange): Map<String, List<Skill>> = parserScope {
val sheet = data.values.sheet() val skills = hashMapOf<String, MutableList<Skill>>()
val values = hashMapOf<String, MutableList<Skill>>()
lateinit var sheetStructure: Map<String, Int> sheet.forEachRow { index, row ->
fun List<*>.parse(column: String): String? = when (index) {
(getOrNull(sheetStructure.getValue(column)) as? String)?.takeIf { it.isNotBlank() } 0 -> updateStructure(row = row, columns = COLUMNS)
else -> {
sheet?.forEachIndexed { index, row ->
when {
index == 0 -> {
sheetStructure = row.checkSheetStructure(model = COLUMNS)
}
row is List<*> -> {
val character = row[0] as? String val character = row[0] as? String
val name = row.parse(column = NAME) val name = row.parse(column = NAME)
if (character?.isNotBlank() == true && name != null) { if (character?.isNotBlank() == true && name != null) {
@ -37,28 +26,22 @@ class SkillParser @Inject constructor(
cost = row.parse(column = COST), cost = row.parse(column = COST),
effect = throwParser.parse(row.parse(column = EFFECT)), effect = throwParser.parse(row.parse(column = EFFECT)),
) )
values.getOrPut(character) { mutableListOf() }.add(skill) skills.getOrPut(character) { mutableListOf() }.add(skill)
} }
} }
} }
} }
return values return@parserScope skills
} }
companion object { companion object {
private const val NAME = "Nom" private val NAME = column("Nom")
private const val AMOUNT = "Quantité" private val AMOUNT = column("Quantité")
private const val REST = "Récupération" private val REST = column("Récupération")
private const val COST = "Coût" private val COST = column("Coût")
private const val EFFECT = "Effect" private val EFFECT = column("Effect")
private val COLUMNS = listOf( private val COLUMNS get() = listOf(NAME, AMOUNT, REST, COST, EFFECT)
NAME,
AMOUNT,
REST,
COST,
EFFECT,
)
} }
} }

View file

@ -4,11 +4,11 @@ import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Alteration import com.pixelized.rplexicon.model.Alteration
import com.pixelized.rplexicon.model.CharacterSheet import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.model.Property import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.repository.parser.roll.DiceParser import com.pixelized.rplexicon.repository.parser.roll.DiceParser
import com.pixelized.rplexicon.repository.parser.roll.FlatValueParser import com.pixelized.rplexicon.repository.parser.roll.FlatValueParser
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
class AlterationParser @Inject constructor( class AlterationParser @Inject constructor(
@ -16,30 +16,22 @@ class AlterationParser @Inject constructor(
private val flatParser: FlatValueParser, private val flatParser: FlatValueParser,
) { ) {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse(values: ValueRange, sheets: List<CharacterSheet>): Map<String, List<Alteration>> { fun parse(
val sheet = values.values.sheet() sheet: ValueRange,
characterSheets: List<CharacterSheet>,
): Map<String, List<Alteration>> = parserScope {
val properties = Property.values() val properties = Property.values()
val data = hashMapOf<String, MutableList<Alteration>>() val alterations = hashMapOf<String, MutableList<Alteration>>()
lateinit var alterationStructure: Map<String, Int> sheet.forEachRow { index, row ->
lateinit var charactersStructure: Map<String, Int> when (index) {
0 -> updateStructure(row = row, columns = COLUMNS + characterSheets.map { column(it.name) })
fun List<*>.parseString(key: String): String? = else -> {
(getOrNull(alterationStructure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() }
sheet?.forEachIndexed { index, row ->
when {
index == 0 -> {
alterationStructure = row.checkSheetStructure(model = COLUMNS)
charactersStructure = row.checkSheetStructure(model = sheets.map { it.name })
}
row is List<*> -> {
// Assume that the name is the first column. // Assume that the name is the first column.
val name = row.getOrNull(0) as? String val name = row.getOrNull(0) as? String
val source = row.parseString(SOURCE) val source = row.parse(column = SOURCE)
val target = row.parseString(TARGET) val target = row.parse(column = TARGET)
val alteration = if (name != null && source != null && target != null) { val alteration = if (name != null && source != null && target != null) {
Alteration( Alteration(
name = name, name = name,
@ -48,8 +40,7 @@ class AlterationParser @Inject constructor(
active = false, active = false,
status = properties status = properties
.mapNotNull { property -> .mapNotNull { property ->
val column = alterationStructure.getValue(property.key) val value = row.parse(column = column(property.key))
val value = row.getOrNull(column) as? String
if (value?.isNotEmpty() == true) { if (value?.isNotEmpty() == true) {
property to parseAlterationStatus(name, value) property to parseAlterationStatus(name, value)
} else { } else {
@ -62,26 +53,26 @@ class AlterationParser @Inject constructor(
null null
} }
if (alteration != null) { if (alteration != null) {
sheets characterSheets
.filter { // check if the alteration is applicable to the character .filter { // check if the alteration is applicable to the character
alteration.target.let { target -> alteration.target.let { target ->
target == ALL || target == it.characterClass || target == it.race || target == it.name target == ALL || target == it.characterClass || target == it.race || target == it.name
} }
} }
.forEach { // check the default alteration state for that character .forEach { // check the default alteration state for that character
val column = charactersStructure.getValue(it.name) val default = row.parseBool(column = column(it.name))
val default = (row.getOrNull(column) as? String)?.toBoolean()
val assignedAlteration = alteration.copy( val assignedAlteration = alteration.copy(
active = default ?: false active = default ?: false
) )
data.getOrPut(it.name) { mutableListOf() }.add(assignedAlteration) alterations.getOrPut(it.name) { mutableListOf() }
.add(assignedAlteration)
} }
} }
} }
} }
} }
return data return@parserScope alterations
} }
private fun parseAlterationStatus(name: String, value: String): Alteration.Status = private fun parseAlterationStatus(name: String, value: String): Alteration.Status =
@ -125,9 +116,9 @@ class AlterationParser @Inject constructor(
private const val DISADVANTAGE = "dis" private const val DISADVANTAGE = "dis"
private const val FAIL = "fail" private const val FAIL = "fail"
private const val TARGET = "Cible" private val TARGET = column("Cible")
private const val SOURCE = "Source" private val SOURCE = column("Source")
private val COLUMNS private val COLUMNS
get() = listOf(SOURCE, TARGET) + Property.values().map { it.key } get() = listOf(SOURCE, TARGET) + Property.values().map { column(it.key) }
} }
} }

View file

@ -1,33 +1,28 @@
package com.pixelized.rplexicon.repository.parser.alteration package com.pixelized.rplexicon.repository.parser.alteration
import com.google.api.services.sheets.v4.model.ValueRange import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
class AssignedAlterationParser @Inject constructor() { class AssignedAlterationParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse(value: ValueRange, characters: List<String>): Map<String, List<String>> { fun parse(
val sheet = value.values.sheet() sheet: ValueRange,
characterNames: List<String>
): Map<String, List<String>> = parserScope {
val status = hashMapOf<String, MutableList<String>>() val status = hashMapOf<String, MutableList<String>>()
lateinit var structure: Map<String, Int>
sheet?.mapNotNull { it as? List<*> }?.forEachIndexed { index, row -> sheet.forEachRow { index, row ->
when (index) { when (index) {
0 -> { 0 -> updateStructure(row = row, columns = characterNames.map { column(it) })
structure = row.checkSheetStructure(model = characters)
}
else -> { else -> {
val alteration = row.getOrNull(0)?.toString() val alteration = row.getOrNull(0)?.toString()
if (alteration != null) { if (alteration != null) {
characters.forEach { character -> characterNames.forEach { character ->
val value = row.getOrNull(structure.getValue(character)) if (row.parseBool(column = column(character)) == true) {
if (value == TRUE) {
status status
.getOrPut(character) { mutableListOf() } .getOrPut(character) { mutableListOf() }
.add(alteration) .add(alteration)
@ -38,10 +33,6 @@ class AssignedAlterationParser @Inject constructor() {
} }
} }
return status return@parserScope status
}
companion object {
private const val TRUE = "TRUE"
} }
} }

View file

@ -2,6 +2,7 @@ package com.pixelized.rplexicon.repository.parser.inventory
import com.google.api.services.sheets.v4.model.ValueRange import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Inventory import com.pixelized.rplexicon.model.Inventory
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import javax.inject.Inject import javax.inject.Inject
@ -9,10 +10,10 @@ import javax.inject.Inject
class InventoryParser @Inject constructor() { class InventoryParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): Map<String, Inventory> = parserScope { fun parse(sheet: ValueRange): Map<String, Inventory> = parserScope {
val inventories = hashMapOf<String, Inventory.Builder>() val inventories = hashMapOf<String, Inventory.Builder>()
data.forEachRow { index, row -> sheet.forEachRow { index, row ->
when { when {
index == 0 -> updateStructure(row = row, columns = COLUMNS) index == 0 -> updateStructure(row = row, columns = COLUMNS)
@ -46,9 +47,9 @@ class InventoryParser @Inject constructor() {
} }
companion object { companion object {
private const val CONTAINER = "Contenant" private val CONTAINER = column("Contenant")
private const val NAME = "Name" private val NAME = column("Name")
private const val AMOUNT = "Quantité" private val AMOUNT = column("Quantité")
private val COLUMNS = listOf(CONTAINER, NAME, AMOUNT) private val COLUMNS = listOf(CONTAINER, NAME, AMOUNT)
} }
} }

View file

@ -1,35 +1,28 @@
package com.pixelized.rplexicon.repository.parser.spell package com.pixelized.rplexicon.repository.parser.spell
import com.google.api.services.sheets.v4.model.ValueRange import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.repository.parser.ThrowParser
import com.pixelized.rplexicon.model.AssignedSpell import com.pixelized.rplexicon.model.AssignedSpell
import com.pixelized.rplexicon.model.Spell import com.pixelized.rplexicon.model.Spell
import com.pixelized.rplexicon.repository.parser.ThrowParser
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
class AssignedSpellParser @Inject constructor( class AssignedSpellParser @Inject constructor(
private val throwParser: ThrowParser, private val throwParser: ThrowParser,
) { ) {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange, spells: List<Spell>): Map<String, List<AssignedSpell>> { fun parse(
val sheet = data.values.sheet() sheet: ValueRange,
spells: List<Spell>,
): Map<String, List<AssignedSpell>> = parserScope {
val spellsBook = spells.associateBy { it.name } val spellsBook = spells.associateBy { it.name }
lateinit var structure: Map<String, Int>
val assignedSpells = hashMapOf<String, MutableList<AssignedSpell>>() val assignedSpells = hashMapOf<String, MutableList<AssignedSpell>>()
sheet.forEachRow { index, row ->
// declare helper method to parse String when (index) {
fun List<*>.parse(key: String): String? = 0 -> updateStructure(row = row, columns = COLUMNS)
(getOrNull(structure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() } else -> {
sheet?.forEachIndexed { index, row ->
when {
index == 0 -> {
structure = row.checkSheetStructure(model = COLUMNS)
}
row is List<*> -> {
val character = row.getOrNull(0) as? String val character = row.getOrNull(0) as? String
val spell = spellsBook[row.parse(NAME)] val spell = spellsBook[row.parse(NAME)]
if (character != null && spell != null) { if (character != null && spell != null) {
@ -46,22 +39,15 @@ class AssignedSpellParser @Inject constructor(
} }
} }
} }
return@parserScope assignedSpells
return assignedSpells
} }
companion object { companion object {
private const val NAME = "Nom" private val NAME = column("Nom")
private const val HIT = "Touché" private val HIT = column("Touché")
private const val EFFECT = "Effet" private val EFFECT = column("Effet")
private const val LEVEL = "Par niveau" private val LEVEL = column("Par niveau")
private val COLUMNS private val COLUMNS get() = listOf(NAME, HIT, EFFECT, LEVEL)
get() = listOf(
NAME,
HIT,
EFFECT,
LEVEL,
)
} }
} }

View file

@ -3,40 +3,27 @@ package com.pixelized.rplexicon.repository.parser.spell
import android.util.Log import android.util.Log
import com.google.api.services.sheets.v4.model.ValueRange import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.model.Spell import com.pixelized.rplexicon.model.Spell
import com.pixelized.rplexicon.repository.parser.column
import com.pixelized.rplexicon.repository.parser.parserScope
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure
import com.pixelized.rplexicon.utilitary.extentions.sheet
import javax.inject.Inject import javax.inject.Inject
class SpellBookParser @Inject constructor() { class SpellBookParser @Inject constructor() {
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun parse(data: ValueRange): List<Spell> { fun parse(sheet: ValueRange): List<Spell> = parserScope {
val spells = mutableListOf<Spell>()
val sheet = data.values.sheet() sheet.forEachRow { index, row ->
lateinit var structure: Map<String, Int>
// declare helper method to parse String
fun List<*>.parse(key: String): String? =
(getOrNull(structure.getValue(key)) as? String)?.takeIf { it.isNotEmpty() }
return sheet?.mapIndexedNotNull { index, row ->
when { when {
index == 0 -> { index == 0 -> updateStructure(row = row, columns = COLUMNS)
structure = row.checkSheetStructure(model = COLUMNS) else -> {
null val name = row.parse(column = NAME)
} val level = row.parseInt(column = LEVEL)
val school = parseSchool(row.parse(column = SCHOOL))
row is List<*> -> { val castingTime = row.parse(column = CASTING_TIME)
val name = row.parse(NAME) val range = row.parse(column = RANGE)
val level = row.parse(LEVEL)?.toIntOrNull() val requirement = row.parse(column = REQUIREMENT)
val school = parseSchool(row.parse(SCHOOL)) val duration = row.parse(column = DURATION)
val castingTime = row.parse(CASTING_TIME)
val range = row.parse(RANGE)
val requirement = row.parse(REQUIREMENT)
val duration = row.parse(DURATION)
val ritual = row.parse(RITUAL)?.toBoolean() ?: false
if (name != null if (name != null
&& level != null && level != null
&& school != null && school != null
@ -45,7 +32,7 @@ class SpellBookParser @Inject constructor() {
&& requirement != null && requirement != null
&& duration != null && duration != null
) { ) {
Spell( val spell = Spell(
name = name, name = name,
level = level, level = level,
school = school, school = school,
@ -53,16 +40,14 @@ class SpellBookParser @Inject constructor() {
range = range, range = range,
requirement = requirement, requirement = requirement,
duration = duration, duration = duration,
ritual = ritual, ritual = row.parseBool(column = RITUAL) ?: false,
) )
} else { spells.add(spell)
null
} }
} }
else -> null
} }
} ?: emptyList() }
return@parserScope spells
} }
private fun parseSchool(value: String?): Spell.School? = try { private fun parseSchool(value: String?): Spell.School? = try {
@ -73,25 +58,16 @@ class SpellBookParser @Inject constructor() {
} }
companion object { companion object {
private const val NAME = "Nom" private val NAME = column("Nom")
private const val SCHOOL = "École" private val SCHOOL = column("École")
private const val LEVEL = "Niveau" private val LEVEL = column("Niveau")
private const val CASTING_TIME = "Temps d'incantation" private val CASTING_TIME = column("Temps d'incantation")
private const val RANGE = "Portée" private val RANGE = column("Portée")
private const val REQUIREMENT = "Composantes" private val REQUIREMENT = column("Composantes")
private const val DURATION = "Durée" private val DURATION = column("Durée")
private const val RITUAL = "Rituel" private val RITUAL = column("Rituel")
private val COLUMNS private val COLUMNS
get() = listOf( get() = listOf(NAME, LEVEL, SCHOOL, CASTING_TIME, RANGE, REQUIREMENT, DURATION, RITUAL)
NAME,
LEVEL,
SCHOOL,
CASTING_TIME,
RANGE,
REQUIREMENT,
DURATION,
RITUAL,
)
} }
} }

View file

@ -1,30 +1,27 @@
package com.pixelized.rplexicon.utilitary.extentions.local package com.pixelized.rplexicon.utilitary.extentions.local
import com.pixelized.rplexicon.repository.parser.Column
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
@Throws(IncompatibleSheetStructure::class) @Throws(IncompatibleSheetStructure::class)
fun Any?.checkSheetStructure(model: List<String>): HashMap<String, Int> { fun Any?.checkSheetStructure(model: List<Column>): Map<Column, Int> {
// check if the row is a list // check if the row is a list
if (this !is ArrayList<*>) { if (this !is ArrayList<*>) {
throw IncompatibleSheetStructure("First row is not a List: $this") throw IncompatibleSheetStructure("First row is not a List: $this")
} }
// parse the first line to find element that we recognize. // parse the first line to find element that we recognize.
val sheetStructure = hashMapOf<String, Int>() val sheetStructure = model.associateWith { key ->
forEachIndexed { index, cell -> indexOfFirst { cell ->
if (cell is String && model.contains(cell)) { key.names.contains(cell)
sheetStructure[cell] = index
} }
} }
// check if we found everything we need. // check if we found everything we need.
when { when {
sheetStructure.size < model.size -> throw IncompatibleSheetStructure( sheetStructure.any { it.value < 0 } -> throw IncompatibleSheetStructure(
message = "Sheet header row does not have enough column: $size.\nstructure: $this\nheader: $sheetStructure" message = "Sheet header row does not have enough column !" + sheetStructure
) .filter { it.value < 0 }.keys
.joinToString(prefix = "\n- Missing column: ") { it.toString() }
sheetStructure.size > model.size -> throw IncompatibleSheetStructure(
message = "Sheet header row does have too mush columns: $size.\nstructure: $this\nheader: $sheetStructure"
) )
} }
return sheetStructure return sheetStructure
} }