Skip to content

Commit

Permalink
feat: Implemneted mutliple noun type suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
angrezichatterbox committed Jan 21, 2025
1 parent 681ff8b commit 99a598e
Show file tree
Hide file tree
Showing 19 changed files with 570 additions and 131 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
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()
}
}
}
}
}
120 changes: 33 additions & 87 deletions app/src/main/java/be/scri/helpers/DatabaseHelper.kt
Original file line number Diff line number Diff line change
@@ -1,35 +1,19 @@
/**
* A helper to facilitate database calls for Scribe keyboard commands.
*
* Copyright (C) 2024 Scribe
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

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 @@ -47,66 +31,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
}

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
}
dbManagers.fileManager.loadDatabaseFile(language)
}

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)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.util.Log

class GenderDataManager(
private val context: Context,
) {
fun findGenderOfWord(
language: String,
jsonData: DataContract?,
): HashMap<String, List<String>> =
when {
jsonData == null -> HashMap()
!context.getDatabasePath("${language}LanguageData.sqlite").exists() -> {
Log.e("MY-TAG", "Database file for $language does not exist.")
HashMap()
}

else -> processGenderData("${language}LanguageData.sqlite", jsonData)
}

private fun processGenderData(
dbPath: String,
jsonData: DataContract,
): HashMap<String, List<String>> =
HashMap<String, List<String>>().also { genderMap ->
context.getDatabasePath(dbPath).path.let { path ->
SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY).use { db ->
when {
hasCanonicalGender(jsonData) ->
processGenders(
db = db,
nounColumn = jsonData.numbers.keys.firstOrNull(),
genderColumn = jsonData.genders.canonical.firstOrNull(),
genderMap = genderMap,
)

hasMasculineFeminine(jsonData) -> {
processGenders(
db = db,
nounColumn = jsonData.genders.masculines.firstOrNull(),
genderMap = genderMap,
defaultGender = "masculine",
)
processGenders(
db = db,
nounColumn = jsonData.genders.feminines.firstOrNull(),
genderMap = genderMap,
defaultGender = "feminine",
)
}

else -> Log.e("MY-TAG", "No valid gender columns found.")
}
}
}
Log.i("MY-TAG", "Found ${genderMap.size} gender entries")
}

private fun hasCanonicalGender(jsonData: DataContract): Boolean =
jsonData.genders.canonical
.firstOrNull()
?.isNotEmpty() == true

private fun hasMasculineFeminine(jsonData: DataContract): Boolean =
jsonData.genders.masculines.isNotEmpty() &&
jsonData.genders.feminines.isNotEmpty()

@Suppress("NestedBlockDepth")
private fun processGenders(
db: SQLiteDatabase,
nounColumn: String?,
genderMap: HashMap<String, List<String>>,
genderColumn: String? = null,
defaultGender: String? = null,
) {
db.rawQuery("SELECT * FROM nouns", null)?.use { cursor ->
val nounIndex = cursor.getColumnIndex(nounColumn)
val genderIndex = genderColumn?.let { cursor.getColumnIndex(it) } ?: -1

if (nounIndex == -1 || (genderColumn != null && genderIndex == -1)) {
Log.e("MY-TAG", "Required columns not found.")
return
}

while (cursor.moveToNext()) {
cursor.getString(nounIndex)?.lowercase()?.takeUnless { it.isEmpty() }?.let { noun ->
val gender =
when {
genderColumn != null -> cursor.getString(genderIndex)
else -> defaultGender
}

if (!gender.isNullOrEmpty()) {
val existingGenders = genderMap[noun]?.toMutableList() ?: mutableListOf()
if (!existingGenders.contains(gender)) {
existingGenders.add(gender)
genderMap[noun] = existingGenders
}
}
}
}
}
Log.i("MY-TAG", genderMap["schild"].toString())
}
}
Loading

0 comments on commit 99a598e

Please sign in to comment.