From 69fe9da01a7a4836ef02bf7da337bf3cc702946d Mon Sep 17 00:00:00 2001 From: makeevrserg Date: Wed, 10 Jul 2024 12:33:53 +0300 Subject: [PATCH] add ui generator --- modules/ui-generator/build.gradle.kts | 21 +++++++ .../ifrmvp/parser/UiGenerator.kt | 7 +++ .../ifrmvp/parser/UiGeneratorImpl.kt | 43 +++++++++++++++ settings.gradle.kts | 1 + web-api/build.gradle.kts | 1 + .../ifrmvp/backend/di/RootModule.kt | 5 +- .../route/key/data/KeyRouteRepository.kt | 55 +++++++++++++++++++ .../ifrmvp/backend/route/key/di/KeyModule.kt | 6 +- .../key/presentation/KeyRouteRegistry.kt | 48 ++-------------- .../ifrmvp/backend/route/ui/di/UiModule.kt | 9 ++- .../route/ui/presentation/UiRouteRegistry.kt | 38 ++++++++----- .../route/ui/presentation/UiSwagger.kt | 3 +- 12 files changed, 175 insertions(+), 62 deletions(-) create mode 100644 modules/ui-generator/build.gradle.kts create mode 100644 modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGenerator.kt create mode 100644 modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGeneratorImpl.kt create mode 100644 web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepository.kt diff --git a/modules/ui-generator/build.gradle.kts b/modules/ui-generator/build.gradle.kts new file mode 100644 index 0000000..50e7d86 --- /dev/null +++ b/modules/ui-generator/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("org.jetbrains.kotlin.jvm") + alias(libs.plugins.kotlin.serialization) +} + +dependencies { + // Exposed + implementation(libs.exposed.core) + implementation(libs.exposed.dao) + implementation(libs.kotlin.coroutines) + implementation(libs.kotlin.serialization.json) + implementation(kotlin("test")) + // Local + implementation(projects.modules.buildKonfig) + implementation(projects.modules.apiStatus) + implementation(projects.modules.core) + implementation(projects.modules.sharedBackendModel) + implementation(projects.modules.sharedUiModel) + implementation(projects.modules.apiStatus) + implementation(projects.modules.parser) +} diff --git a/modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGenerator.kt b/modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGenerator.kt new file mode 100644 index 0000000..e2bd308 --- /dev/null +++ b/modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGenerator.kt @@ -0,0 +1,7 @@ +package com.flipperdevices.ifrmvp.parser + +import com.flipperdevices.ifrmvp.model.PagesLayout + +interface UiGenerator { + fun generate(remoteContent: String): PagesLayout +} diff --git a/modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGeneratorImpl.kt b/modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGeneratorImpl.kt new file mode 100644 index 0000000..a3ea9a0 --- /dev/null +++ b/modules/ui-generator/src/main/kotlin/com/flipperdevices/ifrmvp/parser/UiGeneratorImpl.kt @@ -0,0 +1,43 @@ +package com.flipperdevices.ifrmvp.parser + +import com.flipperdevices.bridge.dao.api.model.FlipperFileFormat +import com.flipperdevices.ifrmvp.model.IfrButton +import com.flipperdevices.ifrmvp.model.IfrKeyIdentifier +import com.flipperdevices.ifrmvp.model.PageLayout +import com.flipperdevices.ifrmvp.model.PagesLayout +import com.flipperdevices.ifrmvp.model.buttondata.TextButtonData +import com.flipperdevices.infrared.editor.viewmodel.InfraredKeyParser + +class UiGeneratorImpl : UiGenerator { + override fun generate(remoteContent: String): PagesLayout { + val signals = remoteContent + .let(FlipperFileFormat.Companion::fromFileContent) + .let(InfraredKeyParser::mapParsedKeyToInfraredRemotes) + val chunks = signals.chunked(MAX_ROWS * MAX_COLUMNS) { it } + return PagesLayout( + pages = chunks.map { signals -> + var x = -1 + PageLayout( + buttons = signals.map { signal -> + x += 1 + IfrButton( + data = TextButtonData( + keyIdentifier = IfrKeyIdentifier.Name(signal.name), + text = signal.name + ), + position = IfrButton.Position( + y = (x / MAX_COLUMNS) % MAX_ROWS, + x = x % MAX_COLUMNS + ) + ) + } + ) + } + ) + } + + companion object { + private const val MAX_COLUMNS = 5 + private const val MAX_ROWS = 11 + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index f491d10..5908112 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,5 +26,6 @@ include(":modules:core") include(":modules:api-status") include(":modules:build-konfig") include(":modules:parser") +include(":modules:ui-generator") // Master include("web-api") diff --git a/web-api/build.gradle.kts b/web-api/build.gradle.kts index 5868f96..4b9c5a3 100644 --- a/web-api/build.gradle.kts +++ b/web-api/build.gradle.kts @@ -44,6 +44,7 @@ dependencies { implementation(projects.modules.core) implementation(projects.modules.apiStatus) implementation(projects.modules.parser) + implementation(projects.modules.uiGenerator) } application { diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/di/RootModule.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/di/RootModule.kt index c85bb76..e6570c0 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/di/RootModule.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/di/RootModule.kt @@ -45,7 +45,10 @@ interface RootModule { KeyModule.Default(signalApiModule) } override val uiModule: UiModule by lazy { - UiModule.Default(signalApiModule) + UiModule.Default( + signalApiModule = signalApiModule, + keyModule = keyModule + ) } } } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepository.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepository.kt new file mode 100644 index 0000000..f76e656 --- /dev/null +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/data/KeyRouteRepository.kt @@ -0,0 +1,55 @@ +package com.flipperdevices.ifrmvp.backend.route.key.data + +import com.flipperdevices.ifrmvp.backend.db.signal.table.BrandTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryTable +import com.flipperdevices.ifrmvp.backend.db.signal.table.IfrFileTable +import com.flipperdevices.ifrmvp.backend.model.IfrFileModel +import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import java.io.File + +interface KeyRouteRepository { + suspend fun getIfrFile(ifrFileId: Long): File +} + +class KeyRouteRepositoryImpl(private val database: Database) : KeyRouteRepository { + override suspend fun getIfrFile(ifrFileId: Long): File { + val ifrFileModel = transaction(database) { + IfrFileTable.selectAll() + .where { IfrFileTable.id eq ifrFileId } + .map { + IfrFileModel( + id = it[IfrFileTable.id].value, + categoryId = it[IfrFileTable.categoryId].value, + brandId = it[IfrFileTable.brandId].value, + fileName = it[IfrFileTable.fileName] + ) + } + .firstOrNull() + ?: error("Ir file with id $ifrFileId not found!") + } + val categoryFolderName = transaction(database) { + CategoryTable.select(CategoryTable.folderName) + .where { CategoryTable.id eq ifrFileModel.categoryId } + .map { it[CategoryTable.folderName] } + .firstOrNull() + ?: error("Category with id ${ifrFileModel.categoryId} not found!") + } + val brandFolderName = transaction(database) { + BrandTable.select(BrandTable.displayName) + .where { BrandTable.id eq ifrFileModel.brandId } + .map { it[BrandTable.displayName] } + .firstOrNull() + ?: error("Brand with id ${ifrFileModel.brandId} not found!") + } + println("Category :$categoryFolderName brand: $brandFolderName ifr: ${ifrFileModel.fileName}") + val file = ParserPathResolver.ifrFile( + category = categoryFolderName, + brand = brandFolderName, + ifrFolderName = ifrFileModel.fileName.replaceFirst(".ir", "") // todo + ) + return file + } +} diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/di/KeyModule.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/di/KeyModule.kt index 182b793..26314f0 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/di/KeyModule.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/di/KeyModule.kt @@ -2,15 +2,19 @@ package com.flipperdevices.ifrmvp.backend.route.key.di import com.flipperdevices.ifrmvp.backend.core.route.RouteRegistry import com.flipperdevices.ifrmvp.backend.db.signal.di.SignalApiModule +import com.flipperdevices.ifrmvp.backend.route.key.data.KeyRouteRepository +import com.flipperdevices.ifrmvp.backend.route.key.data.KeyRouteRepositoryImpl import com.flipperdevices.ifrmvp.backend.route.key.presentation.KeyRouteRegistry interface KeyModule { val registry: RouteRegistry + val keyRouteRepository: KeyRouteRepository class Default(signalApiModule: SignalApiModule) : KeyModule { + override val keyRouteRepository: KeyRouteRepository = KeyRouteRepositoryImpl(signalApiModule.database) override val registry: RouteRegistry by lazy { KeyRouteRegistry( - signalApiModule.database, + keyRouteRepository, ) } } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/presentation/KeyRouteRegistry.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/presentation/KeyRouteRegistry.kt index 9c495ac..9f4979f 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/presentation/KeyRouteRegistry.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/key/presentation/KeyRouteRegistry.kt @@ -1,21 +1,14 @@ package com.flipperdevices.ifrmvp.backend.route.key.presentation import com.flipperdevices.ifrmvp.backend.core.route.RouteRegistry -import com.flipperdevices.ifrmvp.backend.db.signal.table.BrandTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryTable -import com.flipperdevices.ifrmvp.backend.db.signal.table.IfrFileTable import com.flipperdevices.ifrmvp.backend.model.IfrFileContentResponse -import com.flipperdevices.ifrmvp.backend.model.IfrFileModel -import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver +import com.flipperdevices.ifrmvp.backend.route.key.data.KeyRouteRepository import io.github.smiley4.ktorswaggerui.dsl.routing.get import io.ktor.server.response.respond import io.ktor.server.routing.Routing -import org.jetbrains.exposed.sql.Database -import org.jetbrains.exposed.sql.selectAll -import org.jetbrains.exposed.sql.transactions.transaction internal class KeyRouteRegistry( - private val database: Database + private val keyRouteRepository: KeyRouteRepository ) : RouteRegistry { private fun Routing.statusRoute() { @@ -23,41 +16,8 @@ internal class KeyRouteRegistry( path = "key", builder = { with(KeySwagger) { createSwaggerDefinition() } }, body = { - val ifrFileId = context.parameters["ifr_file_id"]?.toLongOrNull() - val ifrFileModel = transaction(database) { - IfrFileTable.selectAll() - .where { IfrFileTable.id eq ifrFileId } - .map { - IfrFileModel( - id = it[IfrFileTable.id].value, - categoryId = it[IfrFileTable.categoryId].value, - brandId = it[IfrFileTable.brandId].value, - fileName = it[IfrFileTable.fileName] - ) - } - .firstOrNull() - ?: error("Ir file with id $ifrFileId not found!") - } - val categoryFolderName = transaction(database) { - CategoryTable.select(CategoryTable.folderName) - .where { CategoryTable.id eq ifrFileModel.categoryId } - .map { it[CategoryTable.folderName] } - .firstOrNull() - ?: error("Category with id ${ifrFileModel.categoryId} not found!") - } - val brandFolderName = transaction(database) { - BrandTable.select(BrandTable.displayName) - .where { BrandTable.id eq ifrFileModel.brandId } - .map { it[BrandTable.displayName] } - .firstOrNull() - ?: error("Brand with id ${ifrFileModel.brandId} not found!") - } - println("Category :$categoryFolderName brand: $brandFolderName ifr: ${ifrFileModel.fileName}") - val file = ParserPathResolver.ifrFile( - category = categoryFolderName, - brand = brandFolderName, - ifrFolderName = ifrFileModel.fileName.replaceFirst(".ir", "") // todo - ) + val ifrFileId = context.parameters["ifr_file_id"]?.toLongOrNull() ?: -1 + val file = keyRouteRepository.getIfrFile(ifrFileId) if (!file.exists()) error("Ifr file doesn't exists! ${file.absolutePath}") val response = IfrFileContentResponse(file.readText()) context.respond(response) diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/di/UiModule.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/di/UiModule.kt index c5cc26f..18591d8 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/di/UiModule.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/di/UiModule.kt @@ -2,15 +2,22 @@ package com.flipperdevices.ifrmvp.backend.route.ui.di import com.flipperdevices.ifrmvp.backend.core.route.RouteRegistry import com.flipperdevices.ifrmvp.backend.db.signal.di.SignalApiModule +import com.flipperdevices.ifrmvp.backend.route.key.di.KeyModule import com.flipperdevices.ifrmvp.backend.route.ui.presentation.UiRouteRegistry +import com.flipperdevices.ifrmvp.parser.UiGeneratorImpl interface UiModule { val registry: RouteRegistry - class Default(signalApiModule: SignalApiModule) : UiModule { + class Default( + signalApiModule: SignalApiModule, + keyModule: KeyModule + ) : UiModule { override val registry: RouteRegistry by lazy { UiRouteRegistry( signalApiModule.database, + keyRouteRepository = keyModule.keyRouteRepository, + uiGenerator = UiGeneratorImpl() ) } } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiRouteRegistry.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiRouteRegistry.kt index 2dd6d1b..ba1ebcc 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiRouteRegistry.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiRouteRegistry.kt @@ -6,9 +6,12 @@ import com.flipperdevices.ifrmvp.backend.db.signal.table.CategoryTable import com.flipperdevices.ifrmvp.backend.db.signal.table.IfrFileTable import com.flipperdevices.ifrmvp.backend.db.signal.table.UiPresetTable import com.flipperdevices.ifrmvp.backend.model.IfrFileModel +import com.flipperdevices.ifrmvp.backend.route.key.data.KeyRouteRepository +import com.flipperdevices.ifrmvp.parser.UiGenerator import com.flipperdevices.ifrmvp.parser.util.ParserPathResolver import io.github.smiley4.ktorswaggerui.dsl.routing.get import io.ktor.http.ContentType +import io.ktor.server.response.respond import io.ktor.server.response.respondText import io.ktor.server.routing.Routing import org.jetbrains.exposed.sql.Database @@ -16,7 +19,9 @@ import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction internal class UiRouteRegistry( - private val database: Database + private val database: Database, + private val keyRouteRepository: KeyRouteRepository, + private val uiGenerator: UiGenerator ) : RouteRegistry { private fun Routing.statusRoute() { @@ -24,7 +29,7 @@ internal class UiRouteRegistry( path = "ui", builder = { with(UiSwagger) { createSwaggerDefinition() } }, body = { - val ifrFileId = context.parameters["ifr_file_id"]?.toLongOrNull() + val ifrFileId = context.parameters["ifr_file_id"]?.toLongOrNull() ?: -1 val ifrFileModel = transaction(database) { IfrFileTable.selectAll() .where { IfrFileTable.id eq ifrFileId } @@ -58,19 +63,24 @@ internal class UiRouteRegistry( .where { UiPresetTable.ifrFileId eq ifrFileId } .map { it[UiPresetTable.fileName] } .firstOrNull() - ?: error("Could not find ui files with ifrid: $ifrFileId") +// ?: error("Could not find ui files with ifrid: $ifrFileId") + } + if (uiFileName != null) { + val uiPresetFile = ParserPathResolver.uiPresetFile( + category = categoryFolderName, + brand = brandFolderName, + presetFileName = uiFileName, + ifrFolderName = ifrFileModel.fileName.replace(".ir", "") + ) + if (!uiPresetFile.exists()) error("Preset file ${uiPresetFile.absolutePath} not exists") + context.respondText( + contentType = ContentType.Application.Json, + text = uiPresetFile.readText() + ) + } else { + val ifrFile = keyRouteRepository.getIfrFile(ifrFileId) + context.respond(uiGenerator.generate(ifrFile.readText())) } - val uiPresetFile = ParserPathResolver.uiPresetFile( - category = categoryFolderName, - brand = brandFolderName, - presetFileName = uiFileName, - ifrFolderName = ifrFileModel.fileName.replace(".ir", "") - ) - if (!uiPresetFile.exists()) error("Preset file ${uiPresetFile.absolutePath} not exists") - context.respondText( - contentType = ContentType.Application.Json, - text = uiPresetFile.readText() - ) } ) } diff --git a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiSwagger.kt b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiSwagger.kt index 6ca3164..652b2c3 100644 --- a/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiSwagger.kt +++ b/web-api/src/main/kotlin/com/flipperdevices/ifrmvp/backend/route/ui/presentation/UiSwagger.kt @@ -1,5 +1,6 @@ package com.flipperdevices.ifrmvp.backend.route.ui.presentation +import com.flipperdevices.ifrmvp.model.PagesLayout import io.github.smiley4.ktorswaggerui.dsl.routes.OpenApiRoute import io.ktor.http.HttpStatusCode @@ -15,7 +16,7 @@ internal object UiSwagger { response { HttpStatusCode.OK to { description = "Content of UI template" - body { + body { description = "Content of UI template" } }