Skip to content

Commit

Permalink
add ui generator
Browse files Browse the repository at this point in the history
  • Loading branch information
makeevrserg committed Jul 10, 2024
1 parent df6d4b9 commit 69fe9da
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 62 deletions.
21 changes: 21 additions & 0 deletions modules/ui-generator/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.flipperdevices.ifrmvp.parser

import com.flipperdevices.ifrmvp.model.PagesLayout

interface UiGenerator {
fun generate(remoteContent: String): PagesLayout
}
Original file line number Diff line number Diff line change
@@ -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
}
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
1 change: 1 addition & 0 deletions web-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ dependencies {
implementation(projects.modules.core)
implementation(projects.modules.apiStatus)
implementation(projects.modules.parser)
implementation(projects.modules.uiGenerator)
}

application {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ interface RootModule {
KeyModule.Default(signalApiModule)
}
override val uiModule: UiModule by lazy {
UiModule.Default(signalApiModule)
UiModule.Default(
signalApiModule = signalApiModule,
keyModule = keyModule
)
}
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,23 @@
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() {
get(
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,30 @@ 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
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() {
get(
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 }
Expand Down Expand Up @@ -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()
)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -15,7 +16,7 @@ internal object UiSwagger {
response {
HttpStatusCode.OK to {
description = "Content of UI template"
body<String> {
body<PagesLayout> {
description = "Content of UI template"
}
}
Expand Down

0 comments on commit 69fe9da

Please sign in to comment.