First commit

This commit is contained in:
2025-01-19 11:07:46 +01:00
commit 8902b17d66
112 changed files with 3476 additions and 0 deletions

114
shared/build.gradle.kts Normal file
View File

@@ -0,0 +1,114 @@
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig
val ktorVersion = "3.0.2"
val exposedVersion = "0.55.0"
val h2Version = "2.3.232"
val hikariCpVersion = "5.1.0"
val flywayVersion = "10.20.1"
val logbackVersion = "1.5.12"
val assertjVersion = "3.26.3"
val restAssuredVersion = "5.5.0"
val junitVersion = "5.11.3"
val psqlVersion = "42.7.4"
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidLibrary)
kotlin("plugin.serialization")
}
repositories {
mavenCentral()
}
dependencies {
}
kotlin {
androidTarget {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
iosX64()
iosArm64()
iosSimulatorArm64()
jvm()
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser {
val rootDirPath = project.rootDir.path
val projectDirPath = project.projectDir.path
commonWebpackConfig {
devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply {
static = (static ?: mutableListOf()).apply {
// Serve sources to debug inside browser
add(rootDirPath)
add(projectDirPath)
}
}
}
}
}
sourceSets {
commonMain.dependencies {
// put your Multiplatform dependencies here
implementation(libs.kotlinx.serialization.json.jvm)
implementation(libs.kotlinx.serialization.core)
implementation(libs.io.ktor.ktor.server.core)
implementation(libs.ktor.serialization)
implementation(libs.ktor.ktor.server.netty)
implementation(libs.ktor.server.call.logging)
implementation(libs.ktor.server.default.headers)
implementation(libs.ktor.server.websockets)
implementation(libs.ktor.server.content.negotiation)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.ktor.serialization)
implementation(libs.kotlinx.serialization.core)
implementation(libs.kotlin.stdlib)
implementation(libs.kotliquery)
implementation(libs.h2)
implementation(libs.postgresql)
implementation(libs.exposed.core)
implementation(libs.exposed.dao)
implementation(libs.exposed.jdbc)
implementation(libs.exposed.json)
implementation(libs.hikaricp)
implementation(libs.flyway.core)
implementation(libs.logback)
implementation(libs.kformat)
}
}
}
android {
namespace = "org.neutrino.ktans.shared"
compileSdk = libs.versions.android.compileSdk.get().toInt()
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
defaultConfig {
minSdk = libs.versions.android.minSdk.get().toInt()
}
}

View File

@@ -0,0 +1,9 @@
package org.neutrino.ktans
import android.os.Build
class AndroidPlatform : Platform {
override val name: String = "Android ${Build.VERSION.SDK_INT}"
}
actual fun getPlatform(): Platform = AndroidPlatform()

View File

@@ -0,0 +1,3 @@
package org.neutrino.ktans
const val SERVER_PORT = 8080

View File

@@ -0,0 +1,9 @@
package org.neutrino.ktans
class Greeting {
private val platform = getPlatform()
fun greet(): String {
return "Hello, ${platform.name}!"
}
}

View File

@@ -0,0 +1,7 @@
package org.neutrino.ktans
interface Platform {
val name: String
}
expect fun getPlatform(): Platform

View File

@@ -0,0 +1,18 @@
package dao
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.DictTypes
import models.DictType
class DictTypeDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<DictTypeDao>(DictTypes)
var shortName by DictTypes.shortName
var fullName by DictTypes.fullName
fun toModel() = DictType(
id = id.value,
shortName = shortName,
fullName = fullName
)
}

View File

@@ -0,0 +1,25 @@
package dao
import dao.SuffixDao.Companion.referrersOn
import models.Language
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.*
import models.Dictionary
class DictionaryDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<DictionaryDao>(Dictionaries)
var name by Dictionaries.name
var fullName by Dictionaries.fullName
var lang1 by LanguageDao referencedOn Dictionaries.lang1
var lang2 by LanguageDao referencedOn Dictionaries.lang2
fun toModel(): Dictionary = Dictionary(
id = id.value,
name = name,
fullName = fullName,
lang1Id = lang1.id.value,
lang2Id = lang2.id.value
)
}

View File

@@ -0,0 +1,18 @@
package dao
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.*
import models.Language
class LanguageDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<LanguageDao>(Languages)
var name by Languages.name
var shortName by Languages.shortName
fun toModel() : Language = Language(
id = id.value,
name = name,
shortName = shortName
)
}

View File

@@ -0,0 +1,22 @@
package dao
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.Pronunciations
import models.Pronunciation
class PronunciationDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<PronunciationDao>(Pronunciations)
var ipa by Pronunciations.ipa
var filename by Pronunciations.filename
var type by PronunciationTypeDao referencedOn Pronunciations.typeId
fun toModel() : Pronunciation = Pronunciation(
id = id.value,
typeId = type.id.value,
ipa = ipa,
filename = filename
)
}

View File

@@ -0,0 +1,12 @@
package dao
import models.Translation
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.PronunciationTypes
class PronunciationTypeDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<PronunciationTypeDao>(PronunciationTypes)
var name by PronunciationTypes.name
}

View File

@@ -0,0 +1,16 @@
package dao
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.Suffixes
import models.Suffix
class SuffixDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<SuffixDao>(Suffixes)
var text by Suffixes.text
fun toModel(): Suffix = Suffix(
id = id.value,
text = text
)
}

View File

@@ -0,0 +1,47 @@
package dao
import dao.SuffixDao.Companion.referrersOn
import models.Language
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.*
import models.Term
import models.TermFull
class TermDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<TermDao>(Terms)
var string1 by Terms.string1
var string2 by Terms.string2
var suffix1 by SuffixDao optionalReferencedOn Terms.suffix1
var suffix2 by SuffixDao optionalReferencedOn Terms.suffix2
var type by DictTypeDao optionalReferencedOn Terms.type
var member by Terms.member
var order2 by Terms.order2
//var flags by Terms.flags
var pronunciations by PronunciationDao via TermsPronunciations
fun toModel(): Term = Term(
id = id.value,
string1 = string1,
string2 = string2,
typeId = type?.id?.value,
suffix1Id = suffix1?.id?.value,
suffix2Id = suffix2?.id?.value,
member = member,
order2 = order2
//flags = flags
)
fun toFullModel(): TermFull = TermFull(
id = id.value,
string1 = string1,
string2 = string2,
type = type?.toModel(),
suffix1 = suffix1?.toModel(),
suffix2 = suffix2?.toModel(),
member = member,
order2 = order2,
//flags = flags,
pronunciations = pronunciations.map { it.toModel() }
)
}

View File

@@ -0,0 +1,27 @@
package dao
import models.Translation
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import tables.Translations
class TranslationDao(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<TranslationDao>(Translations)
var langName1 by Translations.langName1
var langName2 by Translations.langName2
var direction by Translations.direction
var dictionary by DictionaryDao referencedOn Translations.dictionaryId
var lang1 by LanguageDao referencedOn Translations.lang1Id
var lang2 by LanguageDao referencedOn Translations.lang2Id
fun toModel() : Translation = Translation(
id = id.value,
dictionaryId = dictionary.id.value,
lang1Id = lang1.id.value,
lang2Id = lang2.id.value,
lang1Name = langName1,
lang2Name = langName2,
direction = direction
)
}

View File

@@ -0,0 +1,197 @@
package db.migration
import org.flywaydb.core.api.migration.BaseJavaMigration
import org.flywaydb.core.api.migration.Context
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.transactions.transaction
import kotlin.collections.*
import kotliquery.*
import de.m3y.kformat.*
import de.m3y.kformat.Table.Hints
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.Json
val format = Json { encodeDefaults = true }
data class Dictionary(
val id: Int,
val lang1Id: Int,
val lang2Id: Int,
val name: String,
val fullName: String)
data class DictType(
val id: Int,
val shortName: String,
val fullName: String
)
data class Language(
val id: Int,
val name: String,
val shortName: String
)
data class Translation(
val id: Int,
val dictionaryId: Int,
val lang1Id: Int,
val lang2Id: Int,
val lang1Name: String,
val lang2Name: String,
val direction: Int
)
data class Pronunciation(
val id: Int,
val typeId: Int,
val ipa: String?,
val filename: String?
)
data class PronunciationType(
val id: Int,
val name: String?
)
data class TermsPronunciation(
val termId: Int,
val pronunciationId: Int,
)
data class Suffix(
val id: Int,
val text: String
)
data class Term(
val id: Int,
val dictionaryId: Int,
val string1: String,
val string2: String,
val suffix1Id: Int?,
val suffix2Id: Int?,
val typeId: Int?,
val member: String?,
val order2: Int?,
val flags: String?
)
val toDictionary: (Row) -> Dictionary = { row ->
Dictionary(
row.int("id"),
row.int("lang1_id"),
row.int("lang2_id"),
row.string("name"),
row.string("full_name")
)
}
val toDictType: (Row) -> DictType = { row ->
DictType(
row.int("id"),
row.string("short_name"),
row.string("full_name")
)
}
val toLanguage: (Row) -> Language = { row ->
Language(
row.int("id"),
row.string("name"),
row.string("short_name")
)
}
val toTranslation: (Row) -> Translation = { row ->
Translation(
row.int("id"),
row.int("dictionary_id"),
row.int("lang1_id"),
row.int("lang2_id"),
row.string("lang_name1"),
row.string("lang_name2"),
row.int("direction"),
)
}
val toPronunciation: (Row) -> Pronunciation = { row ->
Pronunciation(
row.int("id"),
row.int("type_id"),
row.stringOrNull("ipa"),
row.stringOrNull("filename")
)
}
val toTermsPronunciation: (Row) -> TermsPronunciation = { row ->
TermsPronunciation(
row.int("term_id"),
row.int("pronunciation_id"),
)
}
val toSuffix: (Row) -> Suffix = { row ->
Suffix(
row.int("id"),
row.string("text")
)
}
val toTerm: (Row) -> Term = { row ->
Term(
row.int("id"),
row.int("dictionary_id"),
row.string("string1"),
row.string("string2"),
row.intOrNull("suffix1_id"),
row.intOrNull("suffix2_id"),
row.intOrNull("type_id"),
row.stringOrNull("member"),
row.intOrNull("order2"),
row.stringOrNull("flags")
)
}
object SharedData {
var hashDict: Map<Int,Dictionary> = mapOf()
var hashDictType: Map<Int,DictType> = mapOf()
var hashLang: Map<Int,Language> = mapOf()
var hashTransMap: Map<Int,Translation> = mapOf()
var hashPron: Map<Int,Pronunciation> = mapOf()
var hashSuffix: Map<Int,Suffix> = mapOf()
var hashTermPron : MutableMap<Int,Array<Int>> = mutableMapOf()
var listTerm : List<Term> = listOf()
var getListofTerms : (Int) -> List<Term> = {
listOf()
}
}
class V1__test_connection: BaseJavaMigration() {
override fun migrate(context: Context?) {
val session = sessionOf("jdbc:postgresql://nuc.lan:5432/dict", "dict_user", "PW4dbdict")
val allNameQuery = queryOf("select * from dictionary").map(toDictionary).asList
val allDict = session.run(allNameQuery)
SharedData.hashDict = allDict.associateBy { it.id }
val allDictTypeQuery = queryOf("select * from dict_type").map(toDictType).asList
val allDictType = session.run(allDictTypeQuery)
SharedData.hashDictType = allDictType.associateBy { it.id }
val allTranslationQuery = queryOf("select * from translation").map(toTranslation).asList
val allTranslation = session.run(allTranslationQuery)
SharedData.hashTransMap = allTranslation.associateBy { it.id }
val allLangQuery = queryOf("select * from language").map(toLanguage).asList
val allLanguages = session.run(allLangQuery)
SharedData.hashLang = allLanguages.associateBy { it.id }
val allSuffixQuery = queryOf("select * from suffix").map(toSuffix).asList
val allSuffix = session.run(allSuffixQuery)
SharedData.hashSuffix = allSuffix.associateBy { it.id }
val allPronunciationQuery = queryOf("select * from pronunciation").map(toPronunciation).asList
val allPronunciation = session.run(allPronunciationQuery)
SharedData.hashPron = allPronunciation.associateBy { it.id }
val allTermsPronunciationQuery = queryOf("select * from terms_pronunciations").map(toTermsPronunciation).asList
val allTermsPronunciation = session.run(allTermsPronunciationQuery)
allTermsPronunciation.forEach {
val p = SharedData.hashTermPron.getOrDefault(it.termId, arrayOf())
SharedData.hashTermPron[it.termId] = p + it.pronunciationId
}
SharedData.getListofTerms = {
println("DB ID = ${it}")
session.run(queryOf("select * from term WHERE dictionary_id=${it}").map(toTerm).asList)
}
}
}

View File

@@ -0,0 +1,53 @@
package db.migration
import kotlinx.serialization.decodeFromString
import tables.*
import org.flywaydb.core.api.migration.BaseJavaMigration
import org.flywaydb.core.api.migration.Context
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.upsert
class V2__create_settingsdb: BaseJavaMigration() {
override fun migrate(context: Context?) {
transaction {
SchemaUtils.create(Dictionaries, Languages, Translations, Settings)
for ((di, lang) in SharedData.hashLang) {
Languages.insert {
it[id] = lang.id
it[name] = lang.name
it[shortName] = lang.shortName
}
}
for ((di, dict) in SharedData.hashDict) {
Dictionaries.insert {
it[id] = dict.id
it[lang1] = dict.lang1Id
it[lang2] = dict.lang2Id
it[name] = dict.name
it[fullName] = dict.fullName
}
}
for ((di, trans) in SharedData.hashTransMap) {
Translations.insert {
it[id] = trans.id
it[lang1Id] = trans.lang1Id
it[lang2Id] = trans.lang2Id
it[dictionaryId] = trans.dictionaryId
it[langName1] = trans.lang1Name
it[langName2] = trans.lang2Name
it[direction] = trans.direction
}
}
}
}
}

View File

@@ -0,0 +1,145 @@
package db.migration
import dao.DictionaryDao
import kotlinx.serialization.decodeFromString
import tables.*
import org.flywaydb.core.api.migration.BaseJavaMigration
import org.flywaydb.core.api.migration.Context
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
import kotlin.system.exitProcess
class V3__create_dictionaries: BaseJavaMigration() {
override fun migrate(context: Context?) {
println(SharedData.hashDict)
//exitProcess(0)
for ((dbId,dict) in SharedData.hashDict) {
var dictTerms = SharedData.getListofTerms(dbId)
val dbName = "jdbc:h2:file:/Users/jaro/data/dict_${SharedData.hashLang[dict.lang1Id]?.shortName?.lowercase()}${SharedData.hashLang[dict.lang2Id]?.shortName?.lowercase()}"
println(dbName)
val db = Database.connect(dbName)
transaction (db) {
SchemaUtils.create(Dictionaries, Languages, Translations, TermsPronunciations, Pronunciations, PronunciationTypes, DictTypes, Suffixes, Terms)
for ((di, lang) in SharedData.hashLang) {
Languages.insert {
it[id] = lang.id
it[name] = lang.name
it[shortName] = lang.shortName
}
}
for ((di, d) in SharedData.hashDict) {
Dictionaries.insert {
it[id] = d.id
it[lang1] = d.lang1Id
it[lang2] = d.lang2Id
it[name] = d.name
it[fullName] = d.fullName
}
}
for ((di, trans) in SharedData.hashTransMap) {
Translations.insert {
it[id] = trans.id
it[lang1Id] = trans.lang1Id
it[lang2Id] = trans.lang2Id
it[dictionaryId] = trans.dictionaryId
it[langName1] = trans.lang1Name
it[langName2] = trans.lang2Name
it[direction] = trans.direction
}
}
PronunciationTypes.insert {
it[id] = 1
it[name] = "English"
}
PronunciationTypes.insert {
it[id] = 2
it[name] = "American english"
}
PronunciationTypes.insert {
it[id] = 3
it[name] = "Business english"
}
for ((di, dt) in SharedData.hashDictType) {
DictTypes.insert {
it[id] = dt.id
it[shortName] = dt.shortName
it[fullName] = dt.fullName
}
}
var suffixMap : MutableMap<Int,Boolean> = mutableMapOf()
for (t in dictTerms) {
if (t.suffix1Id != null && !suffixMap.containsKey(t.suffix1Id)) {
val ss = SharedData.hashSuffix.getValue(t.suffix1Id)
Suffixes.insert {
it[id] = ss.id
it[text] = ss.text
}
suffixMap[ss.id] = true
}
if (t.suffix2Id != null && !suffixMap.containsKey(t.suffix2Id)) {
val ss = SharedData.hashSuffix.getValue(t.suffix2Id)
Suffixes.insert {
it[id] = ss.id
it[text] = ss.text
}
suffixMap[ss.id] = true
}
Terms.insert {
it[id] = t.id
it[string1] = t.string1
it[string2] = t.string2
it[suffix1] = t.suffix1Id
it[suffix2] = t.suffix2Id
it[type] = t.typeId
it[member] = t.member
it[order2] = t.order2
//it[flags] = format.decodeFromString<IntArray?>(t.flags?.toString() ?:"[]")
}
if (SharedData.hashTermPron.containsKey(t.id)) {
for (pronId in SharedData.hashTermPron.getValue(t.id)){
val pron = SharedData.hashPron.getValue(pronId)
Pronunciations.upsert {
it[id] = pron.id
it[typeId] = pron.typeId
it[ipa] = pron.ipa
it[filename] = pron.filename
}
}
}
}
for (t in dictTerms) {
if (SharedData.hashTermPron.containsKey(t.id)) {
for (pronId in SharedData.hashTermPron.getValue(t.id)){
TermsPronunciations.insert {
it[term] = t.id
it[pronunciation] = pronId
}
}
}
}
dictTerms = listOf()
suffixMap = mutableMapOf()
}
}
}
}

View File

@@ -0,0 +1,14 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class DictType(
val id: Int,
val shortName: String,
val fullName: String
)

View File

@@ -0,0 +1,17 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Dictionary(
val id: Int,
val lang1Id: Int,
val lang2Id: Int,
val name: String?,
val fullName: String?,
val translations: List<Translation> = emptyList()
)

View File

@@ -0,0 +1,15 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Language(
val id: Int,
val name: String,
val shortName: String
)

View File

@@ -0,0 +1,12 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Pronunciation(
val id: Int,
val typeId: Int,
val ipa: String?,
val filename: String?
)

View File

@@ -0,0 +1,10 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class PronunciationType(
val id: Int,
val name: String?
)

View File

@@ -0,0 +1,12 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Settings(
val id: Int,
val dictionary: Int?,
val lastSearch: String?,
)

View File

@@ -0,0 +1,11 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Suffix(
val id: Int,
val text: String
)

View File

@@ -0,0 +1,33 @@
package models
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject
import org.jetbrains.exposed.sql.Table
@Serializable
data class Term(
val id: Int,
val string1: String,
val string2: String,
val typeId: Int?,
val suffix1Id: Int?,
val suffix2Id: Int?,
val member: String?,
val order2: Int?,
// val flags: IntArray?
)
@Serializable
data class TermFull(
val id: Int,
val string1: String,
val string2: String,
val type: DictType?,
val suffix1: Suffix?,
val suffix2: Suffix?,
val member: String?,
val order2: Int?,
//val flags: IntArray?,
val pronunciations: List<Pronunciation>?
)

View File

@@ -0,0 +1,11 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class TermsPronunciation(
val termsId: Int,
val pronunciationId: Int
)

View File

@@ -0,0 +1,17 @@
package models
import kotlinx.serialization.Serializable
import org.jetbrains.exposed.sql.Table
@Serializable
data class Translation(
val id: Int,
val dictionaryId: Int,
val lang1Id: Int,
val lang2Id: Int,
val lang1Name: String?,
val lang2Name: String?,
val direction: Int
)

View File

@@ -0,0 +1,107 @@
package service
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.flywaydb.core.Flyway
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
import org.slf4j.LoggerFactory
import javax.sql.DataSource
import tables.*
import dao.*
import com.typesafe.config.ConfigFactory
import models.Translation
object DatabaseFactory {
private val log = LoggerFactory.getLogger(this::class.java)
private val dbs : MutableMap<Int,Database> = mutableMapOf()
private val hdb : MutableMap<String,Database> = mutableMapOf()
private val htrans: MutableMap<String,Translation> = mutableMapOf()
fun connectAndMigrate() {
log.info("Initialising database")
val pool = hikari()
dbs[0] = Database.connect(pool)
runFlyway(pool)
}
private fun hikari(): HikariDataSource {
val cfg = ConfigFactory.load().getConfig("h2")
val config = HikariConfig().apply {
driverClassName = cfg.getString("driver")
jdbcUrl = String.format(cfg.getString("url"),"dict_settings")
maximumPoolSize = 3
isAutoCommit = false
transactionIsolation = "TRANSACTION_REPEATABLE_READ"
validate()
}
return HikariDataSource(config)
}
fun getTranslationForLanguages(l1: String, l2: String): Translation? = htrans["${l1}${l2}"]?: null
fun connectAll(): Map<Int,Database> {
val hMap : MutableMap<Int, Database> = mutableMapOf()
transaction {
for (trans in TranslationDao.all()) {
val l1 = trans.lang1.shortName.lowercase()
val l2 = trans.lang2.shortName.lowercase()
htrans["${l1}${l2}"] = trans.toModel()
}
for (dict in DictionaryDao.all()) {
val cfg = ConfigFactory.load().getConfig("h2")
val config = HikariConfig().apply {
driverClassName = cfg.getString("driver")
jdbcUrl = String.format(cfg.getString("url"),"dict_${dict.lang1.shortName.lowercase()}${dict.lang2.shortName.lowercase()}")
maximumPoolSize = 3
isAutoCommit = false
transactionIsolation = "TRANSACTION_REPEATABLE_READ"
validate()
}
val db = HikariDataSource(config)
val dbc = Database.connect(db)
dbs[dict.id.value] = dbc
hdb["${dict.lang1.shortName.lowercase()}${dict.lang2.shortName.lowercase()}"] = dbc
}
}
return hMap
}
private fun runFlyway(datasource: DataSource) {
val flyway = Flyway.configure().dataSource(datasource).load()
try {
flyway.info()
flyway.migrate()
} catch (e: Exception) {
log.error("Exception running flyway migration", e)
throw e
}
log.info("Flyway migration has finished")
}
suspend fun <T> dbExec(
block: () -> T
): T = withContext(Dispatchers.IO) {
transaction { block() }
}
fun <T> dbExecId(
dbId: Int, block: () -> T
) : T = transaction(dbs[dbId]) {
block()
}
fun <T> dbExecName(
d1: String, d2: String, block: () -> T
) : T = transaction(hdb["${d1.lowercase()}${d2.lowercase()}"]) {
block()
}
}

View File

@@ -0,0 +1,9 @@
package service
import dao.DictTypeDao
import models.DictType
interface DictTypeService {
fun getDictType(id: Int): DictType?
fun getAllDictTypes(): List<DictType>
}

View File

@@ -0,0 +1,17 @@
package service
import dao.DictTypeDao
import org.jetbrains.exposed.sql.transactions.transaction
import service.DatabaseFactory.dbExecId
import models.DictType
class DictTypeServiceImpl : DictTypeService {
override fun getDictType(id: Int): DictType? = dbExecId(1) {
DictTypeDao.findById(id)?.toModel()
}
override fun getAllDictTypes(): List<DictType> = dbExecId(1) {
DictTypeDao.all().map { it.toModel() }
}
}

View File

@@ -0,0 +1,9 @@
package service
import dao.DictionaryDao
import models.Dictionary
interface DictionaryService {
fun getDictionary(id: Int): Dictionary?
fun getAllDictionary(): List<Dictionary>
}

View File

@@ -0,0 +1,16 @@
package service
import dao.DictionaryDao
import org.jetbrains.exposed.sql.transactions.transaction
import service.DatabaseFactory.dbExecId
import models.Dictionary
class DictionaryServiceImpl : DictionaryService {
override fun getDictionary(id: Int): Dictionary? = dbExecId(1) {
DictionaryDao.findById(id)?.toModel()
}
override fun getAllDictionary(): List<Dictionary> = dbExecId(1) {
DictionaryDao.all().map { it.toModel() }
}
}

View File

@@ -0,0 +1,9 @@
package service
import dao.LanguageDao
import models.Language
interface LanguageService {
fun getLanguage(id: Int): Language?
fun getAllLanguages(): List<Language>
}

View File

@@ -0,0 +1,17 @@
package service
import dao.LanguageDao
import org.jetbrains.exposed.sql.transactions.transaction
import service.DatabaseFactory.dbExecId
import models.Language
class LanguageServiceImpl : LanguageService {
override fun getLanguage(id: Int): Language? = dbExecId(1) {
LanguageDao.findById(id)?.toModel()
}
override fun getAllLanguages(): List<Language> = dbExecId(1) {
LanguageDao.all().map { it.toModel() }
}
}

View File

@@ -0,0 +1,9 @@
package service
import dao.PronunciationDao
import models.Pronunciation
interface PronunciationService {
fun getPronunciation(id: Int): Pronunciation?
fun getAllPronunciations(): List<Pronunciation>
}

View File

@@ -0,0 +1,15 @@
package service
import dao.PronunciationDao
import models.Pronunciation
import service.DatabaseFactory.dbExecId
class PronunciationServiceImpl: PronunciationService {
override fun getPronunciation(id: Int): Pronunciation? = dbExecId(1) {
PronunciationDao.findById(id)?.toModel()
}
override fun getAllPronunciations(): List<Pronunciation> = dbExecId(1) {
PronunciationDao.all().map { it.toModel() }
}
}

View File

@@ -0,0 +1,24 @@
package service
import dao.TermDao
import models.Term
import models.TermFull
import models.Pronunciation
import models.Translation
enum class SearchType {
EXACT,
START,
END,
FULL
}
interface TermService {
fun getTerm(id: Int): Term?
fun getFullTerm(id: Int): TermFull?
fun getAllTerm(): List<Term>
fun getPronunciationsForTerm(id: Int): List<Pronunciation>?
fun getTranslationForTerm(term: String, trans: Translation, type: SearchType = SearchType.EXACT): List<TermFull>?
fun getTranslationForTerm(term: String, fromLang: String, toLang: String): List<TermFull>?
}

View File

@@ -0,0 +1,59 @@
package service
import dao.TermDao
import org.jetbrains.exposed.sql.transactions.transaction
import service.DatabaseFactory.dbExecId
import service.DatabaseFactory.dbExecName
import models.Term
import models.TermFull
import models.Pronunciation
import models.Translation
import tables.*
import service.SearchType
class TermServiceImpl : TermService {
override fun getTerm(id: Int): Term? = dbExecId(1) {
TermDao.findById(id)?.toModel()
}
override fun getAllTerm(): List<Term> = dbExecId(1) {
TermDao.all().map { it.toModel() }
}
override fun getFullTerm(id: Int): TermFull? = dbExecId(1) {
TermDao.findById(id)?.toFullModel()
}
override fun getPronunciationsForTerm(id: Int): List<Pronunciation>? = dbExecId(1) {
TermDao.findById(id)?.toFullModel()?.pronunciations
}
override fun getTranslationForTerm(term: String,trans: Translation, type: SearchType): List<TermFull>? {
val s : String
when (type) {
SearchType.START -> s = term + "%"
SearchType.END -> s = "%" + term
SearchType.FULL -> s = "%" + term + "%"
SearchType.EXACT -> s = term
}
return dbExecId(trans.dictionaryId) {
if (trans.direction == 1)
if (type == SearchType.EXACT)
TermDao.find { Terms.string1 eq s }.map { it.toFullModel() }
else
TermDao.find { Terms.string1 like s }.map { it.toFullModel() }
else
if (type == SearchType.EXACT)
TermDao.find { Terms.string2 eq s }.map { it.toFullModel() }
else
TermDao.find { Terms.string1 like s }.map { it.toFullModel() }
}
}
override fun getTranslationForTerm(term: String, fromLang: String, toLang: String): List<TermFull>? = dbExecName(fromLang,toLang) {
TermDao.find { Terms.string1 eq term }.map { it.toFullModel() }
}
}

View File

@@ -0,0 +1,10 @@
package service
import dao.TranslationDao
import models.Translation
interface TranslationService {
fun getTranslation(id: Int): Translation?
fun getTranslationFromTo(from: String, to: String): Translation?
fun getAllTranslations(): List<Translation>
}

View File

@@ -0,0 +1,24 @@
package service
import dao.TranslationDao
import dao.LanguageDao
import org.jetbrains.exposed.sql.transactions.transaction
import service.DatabaseFactory.dbExecId
import service.DatabaseFactory.getTranslationForLanguages
import models.Translation
import tables.Translations
import tables.Languages
class TranslationServiceImpl : TranslationService {
override fun getTranslation(id: Int): Translation? = dbExecId(0) {
TranslationDao.findById(id)?.toModel()
}
override fun getAllTranslations(): List<Translation> = dbExecId(0) {
TranslationDao.all().map { it.toModel() }
}
override fun getTranslationFromTo(from: String, to: String): Translation? = dbExecId(0) {
getTranslationForLanguages(from,to)
}
}

View File

@@ -0,0 +1,8 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object DictTypes : IntIdTable() {
val shortName = varchar("short_name", 255)
val fullName = varchar("full_name", 255)
}

View File

@@ -0,0 +1,10 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object Dictionaries : IntIdTable() {
val lang1 = reference("lang1_id", Languages)
val lang2 = reference("lang2_id", Languages)
val name = varchar("name", 255)
val fullName = varchar("full_name", 255)
}

View File

@@ -0,0 +1,12 @@
package tables
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.Column
object Languages : IntIdTable() {
val name = varchar("name", 255)
val shortName = varchar("short_name", 255)
}

View File

@@ -0,0 +1,10 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object Pronunciations : IntIdTable() {
val typeId = reference("type_id",PronunciationTypes)
val ipa = varchar("ipa", 255).nullable()
val filename = varchar("filename", 255).nullable()
}

View File

@@ -0,0 +1,7 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object PronunciationTypes : IntIdTable() {
val name = varchar("name", 255)
}

View File

@@ -0,0 +1,8 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object Settings : IntIdTable() {
val dictionary = reference("dictionary_id",Dictionaries)
val lastSearch = varchar("last_search", 255).nullable()
}

View File

@@ -0,0 +1,7 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object Suffixes : IntIdTable() {
val text = varchar("text", 255)
}

View File

@@ -0,0 +1,17 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.json.json
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import tables.Suffixes
object Terms : IntIdTable() {
val string1 = varchar("string1", 255)
val string2 = varchar("string2", 255)
val suffix1 = reference("suffix1_id",Suffixes).nullable()
val suffix2 = reference("suffix2_id",Suffixes).nullable()
val type = reference("type_id",DictTypes).nullable()
val member = varchar("member", 255).nullable()
val order2 = integer("order2").nullable()
//val flags = json<IntArray>("flags", Json.Default).nullable()
}

View File

@@ -0,0 +1,10 @@
package tables
import org.jetbrains.exposed.sql.Table
object TermsPronunciations : Table() {
val term = reference("term", Terms)
val pronunciation = reference("pronunciation", Pronunciations)
override val primaryKey = PrimaryKey(term, pronunciation, name = "PK_Term_Pronunciation")
}

View File

@@ -0,0 +1,13 @@
package tables
import org.jetbrains.exposed.dao.id.IntIdTable
object Translations : IntIdTable() {
val dictionaryId = reference("dictionary_id",Dictionaries)
val lang1Id = reference("lang1_id",Languages)
val lang2Id = reference("lang2_id",Languages)
val langName1 = varchar("lang_name1", 255)
val langName2 = varchar("lang_name2", 255)
val direction = integer("direction")
}

View File

@@ -0,0 +1,11 @@
package util
import kotlinx.serialization.json.Json
object JsonMapper {
val defaultMapper = Json {
prettyPrint = true
}
}

View File

@@ -0,0 +1,29 @@
package web
import io.ktor.http.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import service.DictTypeService
import service.DictTypeServiceImpl
fun Route.dictType(dictTypeService: DictTypeServiceImpl) {
route("/dict-type") {
get {
call.respond(dictTypeService.getAllDictTypes())
}
get("/{id}") {
val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id")
val dictType = dictTypeService.getDictType(id)
if (dictType == null) call.respond(HttpStatusCode.NotFound)
else call.respond(dictType)
}
}
}

View File

@@ -0,0 +1,29 @@
package web
import io.ktor.http.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import service.DictionaryServiceImpl
import service.DictionaryService
fun Route.dictionary(dictionaryService: DictionaryServiceImpl) {
route("/dictionary") {
get {
call.respond(dictionaryService.getAllDictionary())
}
get("/{id}") {
val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id")
val dictionary = dictionaryService.getDictionary(id)
if (dictionary == null) call.respond(HttpStatusCode.NotFound)
else call.respond(dictionary)
}
}
}

View File

@@ -0,0 +1,14 @@
package web
import io.ktor.http.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun Route.index() {
val indexPage = javaClass.getResource("/index.html").readText()
get("/") {
call.respondText(indexPage, ContentType.Text.Html)
}
}

View File

@@ -0,0 +1,29 @@
package web
import io.ktor.http.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import service.LanguageServiceImpl
import service.LanguageService
fun Route.language(LanguageService: LanguageServiceImpl) {
route("/language") {
get {
call.respond(LanguageService.getAllLanguages())
}
get("/{id}") {
val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id")
val language = LanguageService.getLanguage(id)
if (language == null) call.respond(HttpStatusCode.NotFound)
else call.respond(language)
}
}
}

View File

@@ -0,0 +1,29 @@
package web
import io.ktor.http.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import service.PronunciationServiceImpl
import service.DictionaryService
fun Route.pronunciation(pronunciationService: PronunciationServiceImpl) {
route("/pronunciation") {
get {
call.respond(pronunciationService.getAllPronunciations())
}
get("/{id}") {
val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id")
val pronunciation = pronunciationService.getPronunciation(id)
if (pronunciation == null) call.respond(HttpStatusCode.NotFound)
else call.respond(pronunciation)
}
}
}

View File

@@ -0,0 +1,37 @@
package web
import io.ktor.http.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import service.TermServiceImpl
import service.TermService
fun Route.term(TermService: TermServiceImpl) {
route("/term") {
get {
call.respond(TermService.getAllTerm())
}
get("/{id}") {
val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id")
val term = TermService.getTerm(id)
if (term == null) call.respond(HttpStatusCode.NotFound)
else call.respond(term)
}
get("/{id}/full") {
val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id")
val term = TermService.getFullTerm(id)
if (term == null) call.respond(HttpStatusCode.NotFound)
else call.respond(term)
}
}
}

View File

@@ -0,0 +1,61 @@
package web
import io.ktor.http.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import service.TranslationServiceImpl
import service.TranslationService
import service.TermServiceImpl
import service.TermService
import service.DatabaseFactory.getTranslationForLanguages
import service.SearchType
fun Route.translation(TranslationService: TranslationServiceImpl,TermService: TermServiceImpl) {
route("/translation") {
get {
call.respond(TranslationService.getAllTranslations())
}
get("/{id}") {
val id = call.parameters["id"]?.toInt() ?: throw IllegalStateException("Must provide id")
val Translation = TranslationService.getTranslation(id)
if (Translation == null) call.respond(HttpStatusCode.NotFound)
else call.respond(Translation)
}
get("/{from}/{to}/{term}") {
val from = call.parameters["from"]?: throw IllegalStateException("Must provide from")
val to = call.parameters["to"]?: throw IllegalStateException("Must provide to")
val term = call.parameters["term"]?: throw IllegalStateException("Must provide term")
val translation = getTranslationForLanguages(from,to)
if (translation == null) call.respond(HttpStatusCode.NotFound)
else {
val terms = TermService.getTranslationForTerm(term,translation)
if (terms == null) call.respond(HttpStatusCode.NotFound)
else call.respond(terms)
}
}
get("/{from}/{to}/{term}/all") {
val from = call.parameters["from"]?: throw IllegalStateException("Must provide from")
val to = call.parameters["to"]?: throw IllegalStateException("Must provide to")
val term = call.parameters["term"]?: throw IllegalStateException("Must provide term")
val translation = getTranslationForLanguages(from,to)
if (translation == null) call.respond(HttpStatusCode.NotFound)
else {
val terms = TermService.getTranslationForTerm(term,translation,SearchType.START)
if (terms == null) call.respond(HttpStatusCode.NotFound)
else call.respond(terms)
}
}
}
}

View File

@@ -0,0 +1,9 @@
package org.neutrino.ktans
import platform.UIKit.UIDevice
class IOSPlatform: Platform {
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}
actual fun getPlatform(): Platform = IOSPlatform()

View File

@@ -0,0 +1,7 @@
package org.neutrino.ktans
class JVMPlatform: Platform {
override val name: String = "Java ${System.getProperty("java.version")}"
}
actual fun getPlatform(): Platform = JVMPlatform()

View File

@@ -0,0 +1,7 @@
package org.neutrino.ktans
class WasmPlatform: Platform {
override val name: String = "Web with Kotlin/Wasm"
}
actual fun getPlatform(): Platform = WasmPlatform()