Skip to content

Commit

Permalink
Merge pull request #300 from angrezichatterbox/feature/data-contract-…
Browse files Browse the repository at this point in the history
…based-suggestion

Implement Data Contract-Based Noun Suggestion with Plural and Gender Handling
  • Loading branch information
andrewtavis authored Jan 24, 2025
2 parents 0493e1b + 472128f commit eb7240c
Show file tree
Hide file tree
Showing 23 changed files with 652 additions and 129 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ plugins {
id("de.mannodermaus.android-junit5") version "1.11.2.0"
id("org.jetbrains.kotlin.plugin.compose") version "2.0.0"
id("jacoco")
kotlin("plugin.serialization") version "1.9.0"
}

jacoco {
Expand Down Expand Up @@ -247,6 +248,7 @@ dependencies {
api("com.google.code.gson:gson:2.10.1")
api("com.github.bumptech.glide:glide:4.14.2")
ksp("com.github.bumptech.glide:ksp:4.14.2")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
}

tasks.register<Copy>("moveFromi18n") {
Expand Down
12 changes: 6 additions & 6 deletions app/src/main/assets/data-contracts/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@
},
"3": {
"title": "Perfekt",
"1": { "ich": "" },
"2": { "du": "" },
"3": { "er/sie/es": "" },
"4": { "wir": "" },
"5": { "ihr": "" },
"6": { "sie/Sie": "" }
"1": { "ich": "[auxiliaryVerb] pastParticiple" },
"2": { "du": "[auxiliaryVerb] pastParticiple" },
"3": { "er/sie/es": "[auxiliaryVerb] pastParticiple" },
"4": { "wir": "[auxiliaryVerb] pastParticiple" },
"5": { "ihr": "[auxiliaryVerb] pastParticiple" },
"6": { "sie/Sie": "[auxiliaryVerb] pastParticiple" }
}
}
}
53 changes: 40 additions & 13 deletions app/src/main/assets/data-contracts/en.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,48 @@
{
"numbers": { "singular": "plural" },
"genders": {
"canonical": [],
"feminines": [],
"masculines": [],
"commons": [],
"neuters": []
"genders": {
"canonical": ["NOT_INCLUDED"],
"feminines": ["NOT_INCLUDED"],
"masculines": ["NOT_INCLUDED"],
"commons": ["NOT_INCLUDED"],
"neuters": ["NOT_INCLUDED"]
},
"conjugations": {
"1": {
"title": "",
"1": "",
"2": "",
"3": "",
"4": "",
"5": "",
"6": ""
"title": "Present",
"1": { "I": "simplePresent" },
"2": { "you": "simplePresent" },
"3": { "he/she/it": "simplePresentThirdPersonSingular" },
"4": { "we": "simplePresent" },
"5": { "you all": "simplePresent" },
"6": { "they": "simplePresent" }
},
"2": {
"title": "Past",
"1": { "I": "simplePast" },
"2": { "you": "simplePast" },
"3": { "he/she/it": "simplePast" },
"4": { "we": "simplePast" },
"5": { "you all": "simplePast" },
"6": { "they": "simplePast" }
},
"3": {
"title": "Perfect",
"1": { "I": "presentParticiple simplePast" },
"2": { "you": "presentParticiple simplePast" },
"3": { "he/she/it": "presentParticiple simplePast" },
"4": { "we": "presentParticiple simplePast" },
"5": { "you all": "presentParticiple simplePast" },
"6": { "they": "presentParticiple simplePast" }
},
"4": {
"title": "Past Perfect",
"1": { "I": "pastParticiple simplePast" },
"2": { "you": "pastParticiple simplePast" },
"3": { "he/she/it": "pastParticiple simplePast" },
"4": { "we": "pastParticiple simplePast" },
"5": { "you all": "pastParticiple simplePast" },
"6": { "they": "pastParticiple simplePast" }
}
}
}
24 changes: 24 additions & 0 deletions app/src/main/java/be/scri/helpers/DatabaseFileManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package be.scri.helpers

import android.content.Context
import android.util.Log
import java.io.FileOutputStream

class DatabaseFileManager(
private val context: Context,
) {
fun loadDatabaseFile(language: String) {
val databaseName = "${language}LanguageData.sqlite"
val dbFile = context.getDatabasePath(databaseName)
Log.i("ALPHA", "Loaded Database")

if (!dbFile.exists()) {
context.assets.open("data/$databaseName").use { inputStream ->
FileOutputStream(dbFile).use { outputStream ->
inputStream.copyTo(outputStream)
outputStream.flush()
}
}
}
}
}
97 changes: 31 additions & 66 deletions app/src/main/java/be/scri/helpers/DatabaseHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
package be.scri.helpers

import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream

class DatabaseHelper(
private val context: Context,
) : SQLiteOpenHelper(context, null, null, DATABASE_VERSION) {
context: Context,
) : SQLiteOpenHelper(
context,
null,
null,
DATABASE_VERSION,
) {
private val dbManagers = DatabaseManagers(context)

companion object {
private const val DATABASE_VERSION = 1
}
Expand All @@ -34,66 +37,28 @@ class DatabaseHelper(
}

fun loadDatabase(language: String) {
val databaseName = "${language}LanguageData.sqlite"
val dbFile = context.getDatabasePath(databaseName)
if (!dbFile.exists()) {
val inputStream: InputStream = context.assets.open("data/$databaseName")
val outputStream: OutputStream = FileOutputStream(dbFile)

inputStream.copyTo(outputStream)

outputStream.flush()
outputStream.close()
inputStream.close()
}
}

fun getEmojiKeywords(language: String): HashMap<String, MutableList<String>> {
val hashMap = HashMap<String, MutableList<String>>()
val dbFile = context.getDatabasePath("${language}LanguageData.sqlite")
val db = SQLiteDatabase.openDatabase(dbFile.path, null, SQLiteDatabase.OPEN_READONLY)
val cursor = db.rawQuery("SELECT * FROM emoji_keywords", null)

cursor.use {
if (cursor.moveToFirst()) {
do {
val key = cursor.getString(0)
hashMap[key] = getEmojiKeyMaps(cursor)
} while (cursor.moveToNext())
}
}
return hashMap
dbManagers.fileManager.loadDatabaseFile(language)
}

fun getEmojiKeyMaps(cursor: Cursor): MutableList<String> {
val values = mutableListOf<String>()

for (i in 1 until cursor.columnCount) {
values.add(cursor.getString(i))
}
return values
}

fun getNounKeywords(language: String): HashMap<String, MutableList<String>> {
val hashMap = HashMap<String, MutableList<String>>()
val dbFile = context.getDatabasePath("${language}LanguageData.sqlite")
val db = SQLiteDatabase.openDatabase(dbFile.path, null, SQLiteDatabase.OPEN_READONLY)
val cursor = db.rawQuery("SELECT * FROM nouns", null)

cursor.use {
if (cursor.moveToFirst()) {
do {
val key = cursor.getString(0).lowercase()
hashMap[key] = getNounKeyMaps(cursor)
} while (cursor.moveToNext())
}
}
return hashMap
}

fun getNounKeyMaps(cursor: Cursor): MutableList<String> {
val values = mutableListOf<String>()
values.add(cursor.getString(2))
return values
}
fun getRequiredData(language: String): DataContract? =
dbManagers.contractLoader.loadContract(
language,
)

fun getEmojiKeywords(language: String): HashMap<String, MutableList<String>> =
dbManagers.emojiManager.getEmojiKeywords(
language,
)

fun findGenderOfWord(language: String): HashMap<String, List<String>> =
dbManagers.genderManager.findGenderOfWord(
language,
getRequiredData(language),
)

fun checkIfWordIsPlural(language: String): List<String>? =
dbManagers.pluralManager.checkIfWordIsPlural(
language,
getRequiredData(language),
)
}
17 changes: 17 additions & 0 deletions app/src/main/java/be/scri/helpers/DatabaseManagers.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package be.scri.helpers

import ContractDataLoader
import EmojiDataManager
import GenderDataManager
import PluralFormsManager
import android.content.Context

class DatabaseManagers(
context: Context,
) {
val fileManager = DatabaseFileManager(context)
val contractLoader = ContractDataLoader(context)
val emojiManager = EmojiDataManager(context)
val genderManager = GenderDataManager(context)
val pluralManager = PluralFormsManager(context)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

import android.content.Context
import android.util.Log
import kotlinx.serialization.json.Json
import java.io.IOException

class ContractDataLoader(
private val context: Context,
) {
fun loadContract(language: String): DataContract? {
val contractName = "${language.lowercase()}.json"
Log.i("ALPHA", "This is the $language")

return try {
val json = Json { ignoreUnknownKeys = true }
context.assets.open("data-contracts/$contractName").use { contractFile ->
val content = contractFile.bufferedReader().readText()
Log.i("ALPHA", content)
json.decodeFromString<DataContract>(content).also {
Log.i("MY-TAG", it.toString())
}
}
} catch (e: IOException) {
Log.e("MY-TAG", "Error loading contract: $contractName", e)
null
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@


import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase

class EmojiDataManager(
private val context: Context,
) {
fun getEmojiKeywords(language: String): HashMap<String, MutableList<String>> {
val dbFile = context.getDatabasePath("${language}LanguageData.sqlite")
return processEmojiKeywords(dbFile.path)
}

private fun processEmojiKeywords(dbPath: String): HashMap<String, MutableList<String>> {
val hashMap = HashMap<String, MutableList<String>>()
val db = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY)

db.use { database ->
database.rawQuery("SELECT * FROM emoji_keywords", null).use { cursor ->
processEmojiCursor(cursor, hashMap)
}
}
return hashMap
}

private fun processEmojiCursor(
cursor: Cursor,
hashMap: HashMap<String, MutableList<String>>,
) {
if (!cursor.moveToFirst()) return

do {
val key = cursor.getString(0)
hashMap[key] = getEmojiKeyMaps(cursor)
} while (cursor.moveToNext())
}

private fun getEmojiKeyMaps(cursor: Cursor): MutableList<String> =
MutableList(cursor.columnCount - 1) { index ->
cursor.getString(index + 1)
}
}
Loading

0 comments on commit eb7240c

Please sign in to comment.