Skip to content

Commit

Permalink
Add support for showing the progress of content entry import jobs tha…
Browse files Browse the repository at this point in the history
…t are running on the server.
  • Loading branch information
mikedawson committed Apr 10, 2024
1 parent 1822632 commit 186493b
Show file tree
Hide file tree
Showing 13 changed files with 194 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ import java.util.*
import com.ustadmobile.core.logging.LogbackAntiLog
import com.ustadmobile.core.util.UMFileUtil
import com.ustadmobile.door.log.NapierDoorLogger
import com.ustadmobile.lib.rest.domain.contententry.importcontent.ImportContentEntryJobStatus
import com.ustadmobile.lib.rest.domain.person.bulkadd.BulkAddPersonRoute
import com.ustadmobile.libcache.headers.FileMimeTypeHelperImpl
import com.ustadmobile.libcache.headers.MimeTypeHelper
Expand Down Expand Up @@ -701,6 +702,13 @@ fun Application.umRestApplication(
)
}

route("contententryimportjob"){
ImportContentEntryJobStatus(
json = di.direct.instance(),
dbFn = { call -> di.on(call).direct.instance(tag = DoorTag.TAG_DB) }
)
}

route("person") {
route("bulkadd") {
BulkAddPersonRoute(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.ustadmobile.lib.rest.domain.contententry.importcontent

import com.ustadmobile.core.db.UmAppDatabase
import com.ustadmobile.lib.db.composites.ContentEntryImportJobProgress
import io.ktor.http.ContentType
import io.ktor.server.application.ApplicationCall
import io.ktor.server.application.call
import io.ktor.server.response.respondText
import io.ktor.server.routing.Route
import io.ktor.server.routing.get
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json

fun Route.ImportContentEntryJobStatus(
json: Json,
dbFn: (ApplicationCall) -> UmAppDatabase,
) {
get("importjobs") {
val contentEntryUid = call.request.queryParameters["contententryuid"]?.toLong() ?: 0
val db = dbFn(call)
val inProgressJobs = db.contentEntryImportJobDao.findInProgressJobsByContentEntryUidAsync(
contentEntryUid
)

call.respondText(
contentType = ContentType.Application.Json,
text = json.encodeToString(
ListSerializer(ContentEntryImportJobProgress.serializer()),
inProgressJobs
),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.ustadmobile.mui.components

import com.ustadmobile.core.hooks.useStringProvider
import mui.material.ListItem
import mui.material.ListItemText
import react.FC
import react.Props
import react.ReactNode
import com.ustadmobile.core.MR
import js.objects.jso
import mui.material.LinearProgress
import mui.material.LinearProgressVariant
import react.create
import react.dom.html.ReactHTML.div


external interface UstadLinearProgressListItemProps: Props {
var progress: Float?
var secondaryContent: ReactNode
var onCancel: () -> Unit
var error: String?
var onDismissError: () -> Unit
}

val UstadLinearProgressListItem = FC<UstadLinearProgressListItemProps> {props ->
val strings = useStringProvider()
val errorVal = props.error
val progressVal = props.progress

ListItem {
if(errorVal != null) {
ListItemText {
primary = ReactNode(strings[MR.strings.import_error])
secondary = ReactNode(errorVal)
}
}else {
ListItemText {
primary = LinearProgress.create {
if(progressVal != null) {
variant = LinearProgressVariant.determinate
this.value = progressVal * 100
}else {
variant = LinearProgressVariant.indeterminate
}
}
secondary = props.secondaryContent

primaryTypographyProps = jso {
component = div
}
secondaryTypographyProps = jso {
component = div
}
}

}

}
}



Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import com.ustadmobile.core.util.UMFileUtil
import com.ustadmobile.core.util.ext.progressBadge
import com.ustadmobile.core.viewmodel.contententry.detailoverviewtab.ContentEntryDetailOverviewUiState
import com.ustadmobile.core.viewmodel.contententry.detailoverviewtab.ContentEntryDetailOverviewViewModel
import com.ustadmobile.core.viewmodel.contententry.detailoverviewtab.progress
import com.ustadmobile.hooks.useUstadViewModel
import com.ustadmobile.lib.db.composites.ContentEntryAndDetail
import com.ustadmobile.lib.db.entities.*
import com.ustadmobile.mui.common.md
import com.ustadmobile.mui.common.xs
import com.ustadmobile.mui.components.UstadLinearProgressListItem
import com.ustadmobile.mui.components.UstadQuickActionButton
import com.ustadmobile.mui.components.UstadRawHtml
import web.cssom.*
Expand All @@ -41,10 +43,10 @@ import mui.icons.material.CheckCircle
import mui.icons.material.Cancel
import mui.icons.material.BookOutlined
import mui.icons.material.EmojiEvents
import mui.icons.material.LocationOnOutlined
import mui.icons.material.CheckBoxOutlined
import mui.icons.material.Delete
import mui.icons.material.Download
import react.ReactNode

val CONTENT_ENTRY_TYPE_ICON_MAP = mapOf(
ContentEntry.TYPE_EBOOK to Book,
Expand All @@ -60,8 +62,6 @@ external interface ContentEntryDetailOverviewScreenProps : Props {

var uiState: ContentEntryDetailOverviewUiState

var onClickDownload: () -> Unit

var onClickOpen: () -> Unit

var onClickMarkComplete: () -> Unit
Expand All @@ -82,19 +82,10 @@ val ContentEntryDetailOverviewComponent2 = FC<ContentEntryDetailOverviewScreenPr
Stack {
spacing = responsive(20.px)

ContentDetails{
ContentDetails {
uiState = props.uiState
}

if (props.uiState.contentEntryButtons?.showDownloadButton == true){
Button{
variant = ButtonVariant.contained
onClick = { props.onClickDownload }

+ strings[MR.strings.download].uppercase()
}
}

if (props.uiState.openButtonVisible){
Button {
onClick = { props.onClickOpen() }
Expand All @@ -104,14 +95,6 @@ val ContentEntryDetailOverviewComponent2 = FC<ContentEntryDetailOverviewScreenPr
}
}

// ContentJobList{
// uiState = props.uiState
// }

if (props.uiState.locallyAvailable) {
LocallyAvailableRow()
}

Divider { orientation = Orientation.horizontal }

QuickActionBarsRow {
Expand All @@ -121,6 +104,14 @@ val ContentEntryDetailOverviewComponent2 = FC<ContentEntryDetailOverviewScreenPr
onClickManageDownload = props.onClickMarkComplete
}

props.uiState.remoteImportJobs.forEach {
UstadLinearProgressListItem {
progress = it.progress
secondaryContent = ReactNode(strings[MR.strings.importing])
error = it.cjiError
}
}

UstadRawHtml {
html = props.uiState.contentEntry?.entry?.description ?: ""
}
Expand Down Expand Up @@ -314,21 +305,6 @@ private val ContentDetailRightColumn = FC <ContentEntryDetailOverviewScreenProps
}
}

private val LocallyAvailableRow = FC <ContentEntryDetailOverviewScreenProps> {

val strings: StringProvider = useStringProvider()

Stack {
direction = responsive(StackDirection.row)
spacing = responsive(10.px)

+ LocationOnOutlined.create()

Typography{
+strings[MR.strings.download_locally_availability]
}
}
}

private val QuickActionBarsRow = FC <ContentEntryDetailOverviewScreenProps> { props ->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.ustadmobile.core.domain.contententry.launchcontent.xapi.LaunchXapiUse
import com.ustadmobile.core.domain.openlink.OpenExternalLinkUseCase
import com.ustadmobile.core.impl.appstate.LoadingUiState
import com.ustadmobile.core.impl.appstate.Snack
import com.ustadmobile.core.util.ext.bodyAsDecodedText
import com.ustadmobile.core.util.ext.localFirstThenRepoIfNull
import com.ustadmobile.core.util.ext.onActiveEndpoint
import com.ustadmobile.door.entities.NodeIdAndAuth
Expand All @@ -28,6 +29,11 @@ import com.ustadmobile.lib.db.composites.ContentEntryImportJobProgress
import com.ustadmobile.lib.db.composites.OfflineItemAndState
import com.ustadmobile.lib.db.composites.TransferJobAndTotals
import io.github.aakira.napier.Napier
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import kotlinx.coroutines.flow.updateAndGet
import kotlinx.serialization.builtins.ListSerializer
import org.kodein.di.instance
import org.kodein.di.instanceOrNull

Expand All @@ -51,6 +57,8 @@ data class ContentEntryDetailOverviewUiState(

val activeImportJobs: List<ContentEntryImportJobProgress> = emptyList(),

val remoteImportJobs: List<ContentEntryImportJobProgress> = emptyList(),

val activeUploadJobs: List<TransferJobAndTotals> = emptyList(),

val offlineItemAndState: OfflineItemAndState? = null,
Expand Down Expand Up @@ -122,6 +130,8 @@ class ContentEntryDetailOverviewViewModel(
private val cancelImportContentEntryUseCase: CancelImportContentEntryUseCase? by
di.onActiveEndpoint().instanceOrNull()

private val httpClient: HttpClient by di.instance()


init {
viewModelScope.launch {
Expand Down Expand Up @@ -193,6 +203,30 @@ class ContentEntryDetailOverviewViewModel(
}
}
}

launch {
try {
do {
val remoteImportJobsJson = httpClient.get(
"${accountManager.activeEndpoint.url}api/contententryimportjob/importjobs"
) {
parameter("contententryuid", entityUidArg.toString())
}.bodyAsDecodedText()
val remoteImportJobs = json.decodeFromString(
ListSerializer(ContentEntryImportJobProgress.serializer()),
remoteImportJobsJson
)

val state = _uiState.updateAndGet { prev ->
prev.copy(
remoteImportJobs = remoteImportJobs
)
}
} while(state.remoteImportJobs.isNotEmpty())
}catch(e: Throwable) {
Napier.d { "Could not get list of jobs" }
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.ustadmobile.libuicompose.view.contententry.detailoverviewtab
package com.ustadmobile.core.viewmodel.contententry.detailoverviewtab

import com.ustadmobile.lib.db.composites.ContentEntryImportJobProgress

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import com.ustadmobile.lib.db.entities.*
ReplicationOperation::class,
PendingRepositorySession::class,

], version = 165)
], version = 166)
expect abstract class UmAppDatabase : RoomDatabase {

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.ustadmobile.core.db.dao
import com.ustadmobile.door.annotation.DoorDao
import androidx.room.Insert
import androidx.room.Query
import com.ustadmobile.core.db.JobStatus
import com.ustadmobile.core.db.dao.ContentEntryImportJobDaoCommon.FIND_IN_PROGRESS_JOBS_BY_CONTENT_ENTRY_UID
import com.ustadmobile.lib.db.composites.ContentEntryImportJobProgress
import com.ustadmobile.lib.db.entities.*
import kotlinx.coroutines.flow.Flow
Expand Down Expand Up @@ -52,19 +52,16 @@ expect abstract class ContentEntryImportJobDao {
""")
abstract suspend fun findByUidAsync(cjiUid: Long): ContentEntryImportJob?

@Query("""
SELECT ContentEntryImportJob.cjiUid,
ContentEntryImportJob.cjiItemProgress,
ContentEntryImportJob.cjiItemTotal,
ContentEntryImportJob.cjiStatus,
ContentEntryImportJob.cjiError
FROM ContentEntryImportJob
WHERE ContentEntryImportJob.cjiContentEntryUid = :contentEntryUid
AND ( ContentEntryImportJob.cjiStatus BETWEEN ${JobStatus.QUEUED} AND ${JobStatus.RUNNING_MAX}
OR (ContentEntryImportJob.cjiStatus = ${JobStatus.FAILED} AND NOT ContentEntryImportJob.cjiErrorDismissed))
""")
@Query(FIND_IN_PROGRESS_JOBS_BY_CONTENT_ENTRY_UID)
abstract fun findInProgressJobsByContentEntryUid(
contentEntryUid: Long,
): Flow<List<ContentEntryImportJobProgress>>


@Query(FIND_IN_PROGRESS_JOBS_BY_CONTENT_ENTRY_UID)
abstract suspend fun findInProgressJobsByContentEntryUidAsync(
contentEntryUid: Long,
): List<ContentEntryImportJobProgress>


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.ustadmobile.core.db.dao

import com.ustadmobile.core.db.JobStatus

object ContentEntryImportJobDaoCommon {

const val FIND_IN_PROGRESS_JOBS_BY_CONTENT_ENTRY_UID = """
SELECT ContentEntryImportJob.cjiUid,
ContentEntryImportJob.cjiItemProgress,
ContentEntryImportJob.cjiItemTotal,
ContentEntryImportJob.cjiStatus,
ContentEntryImportJob.cjiError
FROM ContentEntryImportJob
WHERE ContentEntryImportJob.cjiContentEntryUid = :contentEntryUid
AND ( ContentEntryImportJob.cjiStatus BETWEEN ${JobStatus.QUEUED} AND ${JobStatus.RUNNING_MAX}
OR (ContentEntryImportJob.cjiStatus = ${JobStatus.FAILED} AND NOT ContentEntryImportJob.cjiErrorDismissed))
"""

}
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,14 @@ val MIGRATION_164_165 = DoorMigrationStatementList(164, 165) {
}
}

val MIGRATION_165_166 = DoorMigrationStatementList(165, 166) {
if(it.dbType() == DoorDbType.SQLITE) {
listOf("ALTER TABLE ContentEntryImportJob ADD COLUMN cjiOwnerPersonUid INTEGER NOT NULL DEFAULT 0")
}else {
listOf("ALTER TABLE ContentEntryImportJob ADD COLUMN cjiOwnerPersonUid BIGINT NOT NULL DEFAULT 0")
}
}


fun migrationList() = listOf<DoorMigration>(
MIGRATION_105_106, MIGRATION_106_107,
Expand All @@ -996,6 +1004,7 @@ fun migrationList() = listOf<DoorMigration>(
MIGRATION_151_152, MIGRATION_152_153, MIGRATION_153_154, MIGRATION_154_155,
MIGRATION_156_157, MIGRATION_157_158, MIGRATION_158_159, MIGRATION_159_160,
MIGRATION_160_161, MIGRATION_162_163, MIGRATION_163_164, MIGRATION_164_165,
MIGRATION_165_166,
)


0 comments on commit 186493b

Please sign in to comment.