From 6527e68e5214a654bbe7647595134c8ba6587351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brage=20Skj=C3=B8nborg?= Date: Sat, 16 Aug 2025 17:28:33 +0200 Subject: [PATCH] Ui stuff --- .../mediaprocessing/ui/Configuration.kt | 1 - .../ui/dto/DatabaseEntriesDto.kt | 15 ++++ .../ui/socket/DatabaseEntriesTopic.kt | 68 +++++++++++++++++++ .../ui/socket/ProcesserTasksTopic.kt | 3 +- .../ui/socket/TaskTableTopic.kt | 35 ++++++++++ .../ui/socket/a2a/ProcesserListenerService.kt | 5 +- .../ui/socket/{ => impl}/SocketClient.kt | 2 +- .../ui/socket/{ => impl}/SocketListener.kt | 2 +- .../socket/{ => impl}/SocketMessageHandler.kt | 2 +- apps/ui/web/src/App.tsx | 10 +-- apps/ui/web/src/app/features/types.ts | 17 ++++- apps/ui/web/src/app/page/EventsPage.tsx | 20 +++++- apps/ui/web/src/app/store/work-slice.ts | 1 + 13 files changed, 165 insertions(+), 16 deletions(-) create mode 100644 apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/dto/DatabaseEntriesDto.kt create mode 100644 apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/DatabaseEntriesTopic.kt create mode 100644 apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/TaskTableTopic.kt rename apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/{ => impl}/SocketClient.kt (98%) rename apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/{ => impl}/SocketListener.kt (85%) rename apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/{ => impl}/SocketMessageHandler.kt (93%) diff --git a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/Configuration.kt b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/Configuration.kt index fd3394b6..bfc82415 100644 --- a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/Configuration.kt +++ b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/Configuration.kt @@ -50,7 +50,6 @@ class WebConfig: WebMvcConfigurer { } } }) - } override fun configurePathMatch(configurer: PathMatchConfigurer) { diff --git a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/dto/DatabaseEntriesDto.kt b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/dto/DatabaseEntriesDto.kt new file mode 100644 index 00000000..90465eda --- /dev/null +++ b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/dto/DatabaseEntriesDto.kt @@ -0,0 +1,15 @@ +package no.iktdev.mediaprocessing.ui.dto + +import no.iktdev.mediaprocessing.shared.common.contract.data.Event + +data class DatabaseEntriesDelete( + val referenceId: String, + val eventId: String +) + +data class DatabaseEventEntries( + val referenceId: String, + val events: List, + val created: Long, + val lastEventCreated: Long, +) \ No newline at end of file diff --git a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/DatabaseEntriesTopic.kt b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/DatabaseEntriesTopic.kt new file mode 100644 index 00000000..231fcb9f --- /dev/null +++ b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/DatabaseEntriesTopic.kt @@ -0,0 +1,68 @@ +package no.iktdev.mediaprocessing.ui.socket + +import no.iktdev.eventi.data.referenceId +import no.iktdev.eventi.database.executeWithStatus +import no.iktdev.eventi.database.toEpochSeconds +import no.iktdev.eventi.database.withDirtyRead +import no.iktdev.eventi.database.withTransaction +import no.iktdev.mediaprocessing.shared.common.database.cal.toEvent +import no.iktdev.mediaprocessing.shared.common.database.tables.events +import no.iktdev.mediaprocessing.ui.dto.DatabaseEntriesDelete +import no.iktdev.mediaprocessing.ui.dto.DatabaseEventEntries +import no.iktdev.mediaprocessing.ui.eventDatabase +import no.iktdev.mediaprocessing.ui.eventsManager +import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.and +import org.jetbrains.exposed.sql.deleteWhere +import org.jetbrains.exposed.sql.selectAll +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.messaging.handler.annotation.MessageMapping +import org.springframework.messaging.handler.annotation.Payload +import org.springframework.messaging.simp.SimpMessagingTemplate +import org.springframework.stereotype.Controller + +@Controller +class DatabaseEntriesTopic( + @Autowired private val template: SimpMessagingTemplate? +) { + + private fun push(destination: String, payload: Any) = template?.convertAndSend(destination, payload) + private fun push(payload: Any) = template?.convertAndSend(payload) + + @MessageMapping("/database/events/pull") + fun pullAllDatabaseEvents() { + val result = withDirtyRead(eventDatabase.database) { + events.selectAll().toEvent() + .groupBy { it.referenceId() }.onEach { (t, u) -> + u.sortedBy { x -> x.metadata.created } + } + } ?: emptyMap() + result.map { it -> DatabaseEventEntries( + referenceId = it.key, + events = it.value, + created = it.value.first().metadata.created.toEpochSeconds() * 1000L, + lastEventCreated = it.value.last().metadata.created.toEpochSeconds() * 1000L + ) }.also { + push(it) + } + } + + @MessageMapping("/database/events/delete") + fun deleteEvent(@Payload payload: DatabaseEntriesDelete) { + val status = executeWithStatus (eventDatabase.database) { + events.deleteWhere { + (referenceId eq payload.referenceId) and + (eventId eq payload.eventId) + } + } + if (status) { + pullAllDatabaseEvents() + } + } + + @MessageMapping("/database/tasks") + fun pullAllDatabaseTasks() { + + } + +} \ No newline at end of file diff --git a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/ProcesserTasksTopic.kt b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/ProcesserTasksTopic.kt index 1e295de3..dacf669d 100644 --- a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/ProcesserTasksTopic.kt +++ b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/ProcesserTasksTopic.kt @@ -5,7 +5,6 @@ import no.iktdev.eventi.data.referenceId import no.iktdev.eventi.database.toEpochSeconds import no.iktdev.eventi.database.withDirtyRead import no.iktdev.eventi.database.withTransaction -import no.iktdev.mediaprocessing.shared.common.contract.Events import no.iktdev.mediaprocessing.shared.common.contract.ProcessType import no.iktdev.mediaprocessing.shared.common.contract.data.* import no.iktdev.mediaprocessing.shared.common.contract.dto.OperationEvents @@ -19,6 +18,7 @@ import no.iktdev.mediaprocessing.shared.common.task.TaskType import no.iktdev.mediaprocessing.ui.WebSocketMonitoringService import no.iktdev.mediaprocessing.ui.eventDatabase import no.iktdev.mediaprocessing.ui.socket.a2a.ProcesserListenerService +import no.iktdev.mediaprocessing.ui.socket.impl.SocketListener import org.jetbrains.exposed.sql.selectAll import org.springframework.beans.factory.annotation.Autowired import org.springframework.messaging.handler.annotation.MessageMapping @@ -85,6 +85,7 @@ class ProcesserTasksTopic( val encode: Status = Status.Skipped, val extract: Status = Status.Skipped, val convert: Status = Status.Skipped, + val completed: Status = Status.Awaiting, val created: Long ) {} diff --git a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/TaskTableTopic.kt b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/TaskTableTopic.kt new file mode 100644 index 00000000..e071c2fa --- /dev/null +++ b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/TaskTableTopic.kt @@ -0,0 +1,35 @@ +package no.iktdev.mediaprocessing.ui.socket + +import no.iktdev.eventi.database.withTransaction +import no.iktdev.mediaprocessing.shared.common.database.cal.toTask +import no.iktdev.mediaprocessing.shared.common.database.tables.tasks +import no.iktdev.mediaprocessing.shared.common.task.Task +import no.iktdev.mediaprocessing.ui.eventDatabase +import no.iktdev.mediaprocessing.ui.socket.impl.SocketListener +import org.jetbrains.exposed.sql.selectAll +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.messaging.handler.annotation.MessageMapping +import org.springframework.messaging.simp.SimpMessagingTemplate +import org.springframework.stereotype.Service + +@Service +class TaskTableTopic( + @Autowired private val message: SimpMessagingTemplate?, +) : SocketListener(message) { + + + + data class TaskGroup( + val referenceId: String, + val tasks: List + ) + + @MessageMapping("/taskTable/all") + fun pullAllTasks() { + val result = withTransaction(eventDatabase.database) { + tasks.selectAll().toTask() + .groupBy { it.referenceId }.map { g -> TaskGroup(g.key, g.value) } + } ?: emptyList() + template?.convertAndSend("/topic/taskTable/all", result) + } +} \ No newline at end of file diff --git a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/a2a/ProcesserListenerService.kt b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/a2a/ProcesserListenerService.kt index 77bbb4c5..6e07d36f 100644 --- a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/a2a/ProcesserListenerService.kt +++ b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/a2a/ProcesserListenerService.kt @@ -7,10 +7,9 @@ import no.iktdev.mediaprocessing.shared.common.task.Task import no.iktdev.mediaprocessing.ui.UIEnv import no.iktdev.mediaprocessing.ui.WebSocketMonitoringService import no.iktdev.mediaprocessing.ui.log -import no.iktdev.mediaprocessing.ui.socket.SocketClient -import no.iktdev.mediaprocessing.ui.socket.SocketMessageHandler +import no.iktdev.mediaprocessing.ui.socket.impl.SocketClient +import no.iktdev.mediaprocessing.ui.socket.impl.SocketMessageHandler import org.springframework.beans.factory.annotation.Autowired -import org.springframework.messaging.simp.SimpMessagingTemplate import org.springframework.stereotype.Service @Service diff --git a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketClient.kt b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/impl/SocketClient.kt similarity index 98% rename from apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketClient.kt rename to apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/impl/SocketClient.kt index 6f81b1e5..a794dd8f 100644 --- a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketClient.kt +++ b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/impl/SocketClient.kt @@ -1,4 +1,4 @@ -package no.iktdev.mediaprocessing.ui.socket +package no.iktdev.mediaprocessing.ui.socket.impl import mu.KotlinLogging import org.springframework.messaging.simp.stomp.StompCommand diff --git a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketListener.kt b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/impl/SocketListener.kt similarity index 85% rename from apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketListener.kt rename to apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/impl/SocketListener.kt index 532421f9..5fa9ae4b 100644 --- a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketListener.kt +++ b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/impl/SocketListener.kt @@ -1,4 +1,4 @@ -package no.iktdev.mediaprocessing.ui.socket +package no.iktdev.mediaprocessing.ui.socket.impl import org.springframework.beans.factory.annotation.Autowired import org.springframework.messaging.simp.SimpMessagingTemplate diff --git a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketMessageHandler.kt b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/impl/SocketMessageHandler.kt similarity index 93% rename from apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketMessageHandler.kt rename to apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/impl/SocketMessageHandler.kt index 556684da..5853f9ba 100644 --- a/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/SocketMessageHandler.kt +++ b/apps/ui/src/main/kotlin/no/iktdev/mediaprocessing/ui/socket/impl/SocketMessageHandler.kt @@ -1,4 +1,4 @@ -package no.iktdev.mediaprocessing.ui.socket +package no.iktdev.mediaprocessing.ui.socket.impl import org.springframework.messaging.simp.stomp.StompHeaders import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter diff --git a/apps/ui/web/src/App.tsx b/apps/ui/web/src/App.tsx index 9e748cb9..556ce6cb 100644 --- a/apps/ui/web/src/App.tsx +++ b/apps/ui/web/src/App.tsx @@ -28,6 +28,7 @@ import InputIcon from '@mui/icons-material/Input'; import NotStartedIcon from '@mui/icons-material/NotStarted'; import EventsPage from './app/page/EventsPage'; import TableChartIcon from '@mui/icons-material/TableChart'; +import SubscriptionsIcon from '@mui/icons-material/Subscriptions'; function App() { const client = useStompClient(); @@ -94,21 +95,22 @@ function App() { }}> - window.location.href = "/tasks"} sx={{ + window.location.href = "/events"} sx={{ ...iconHeight }}> - + + } /> } /> } /> } /> diff --git a/apps/ui/web/src/app/features/types.ts b/apps/ui/web/src/app/features/types.ts index 24d2551b..f2074cc5 100644 --- a/apps/ui/web/src/app/features/types.ts +++ b/apps/ui/web/src/app/features/types.ts @@ -3,4 +3,19 @@ export interface CoordinatorOperationRequest { file: string; source: string; mode: "FLOW" | "MANUAL"; -} \ No newline at end of file +} + +export interface Event { + referenceId: string; + eventId: string; + event: string; + data: any; + created: number; +} + +export interface DatabaseEventEntry { + referenceId: string; + events: Array + created: number; + lastEventCreated: number; +} \ No newline at end of file diff --git a/apps/ui/web/src/app/page/EventsPage.tsx b/apps/ui/web/src/app/page/EventsPage.tsx index d63602c4..a04a1987 100644 --- a/apps/ui/web/src/app/page/EventsPage.tsx +++ b/apps/ui/web/src/app/page/EventsPage.tsx @@ -25,7 +25,7 @@ import SimpleTable from "../features/table/sortableTable"; import { UnixTimestamp } from "../features/UxTc"; import ProgressbarWithLabel from "../features/components/ProgressbarWithLabel"; import { LinearProgress, Typography } from "@mui/material"; -import { stat } from "fs"; +import SaveIcon from '@mui/icons-material/Save'; interface RawNodeDatum { name: string; @@ -107,6 +107,10 @@ function renderCustomNodeElement(nodeData: CustomNodeElementProps): JSX.Element workIcon = break; } + case "completed": { + workIcon = + break; + } } @@ -163,6 +167,16 @@ const transformToSteps = (state: ContentEventState): RawNodeDatum => ({ type: "convert", status: state.extract }, + children: [ + { + name: state.referenceId, + attributes: { + type: "completed", + status: state.completed + }, + children: [] + } + ] } ] }, @@ -181,7 +195,7 @@ export default function EventsPage() { const items = events.items.map((event: ContentEventState) => { return { rowId: event.referenceId, - title: event.referenceId, + title: event.title ?? event.referenceId, item: event } as ExpandableItemRow }); @@ -210,7 +224,7 @@ export default function EventsPage() { return (<>