diff --git a/komga-webui/src/types/komga-series.ts b/komga-webui/src/types/komga-series.ts index cc938473a9..78fc02f473 100644 --- a/komga-webui/src/types/komga-series.ts +++ b/komga-webui/src/types/komga-series.ts @@ -7,6 +7,7 @@ interface SeriesDto { booksCount: number, booksReadCount: number, booksUnreadCount: number, + booksInProgressCount: number, metadata: SeriesMetadata } diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt index a68262a3fc..5c87080f47 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt @@ -91,7 +91,8 @@ class SeriesDtoDao( private fun selectBase() = dsl.select(*groupFields) .select(DSL.count(b.ID).`as`("booksCount")) - .select(DSL.count(r.COMPLETED).`as`("booksReadCount")) + .select(DSL.sum(DSL.`when`(r.COMPLETED.isTrue, 1).otherwise(0)).`as`("booksReadCount")) + .select(DSL.sum(DSL.`when`(r.COMPLETED.isFalse, 1).otherwise(0)).`as`("booksInProgressCount")) .from(s) .leftJoin(b).on(s.ID.eq(b.SERIES_ID)) .leftJoin(d).on(s.ID.eq(d.SERIES_ID)) @@ -104,9 +105,11 @@ class SeriesDtoDao( .map { r -> val sr = r.into(s) val dr = r.into(d) - val booksCount = r["booksCount"] as Int - val booksReadCount = r["booksReadCount"] as Int - sr.toDto(booksCount, booksReadCount, dr.toDto()) + val booksCount = r.get("booksCount", Int::class.java) + val booksReadCount = r.get("booksReadCount", Int::class.java) + val booksInProgressCount = r.get("booksInProgressCount", Int::class.java) + val booksUnreadCount = booksCount - booksInProgressCount - booksReadCount + sr.toDto(booksCount, booksReadCount, booksUnreadCount, booksInProgressCount, dr.toDto()) } private fun SeriesSearch.toCondition(): Condition { @@ -119,7 +122,7 @@ class SeriesDtoDao( return c } - private fun SeriesRecord.toDto(booksCount: Int, booksReadCount: Int, metadata: SeriesMetadataDto) = + private fun SeriesRecord.toDto(booksCount: Int, booksReadCount: Int, booksUnreadCount: Int, booksInProgressCount: Int, metadata: SeriesMetadataDto) = SeriesDto( id = id, libraryId = libraryId, @@ -130,6 +133,8 @@ class SeriesDtoDao( fileLastModified = fileLastModified.toUTC(), booksCount = booksCount, booksReadCount = booksReadCount, + booksUnreadCount = booksUnreadCount, + booksInProgressCount = booksInProgressCount, metadata = metadata ) diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/dto/SeriesDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/dto/SeriesDto.kt index 8a179f66fa..17a0506dc6 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/dto/SeriesDto.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/dto/SeriesDto.kt @@ -16,10 +16,10 @@ data class SeriesDto( val fileLastModified: LocalDateTime, val booksCount: Int, val booksReadCount: Int, + val booksUnreadCount: Int, + val booksInProgressCount: Int, val metadata: SeriesMetadataDto -) { - val booksUnreadCount: Int = booksCount - booksReadCount -} +) fun SeriesDto.restrictUrl(restrict: Boolean) = if (restrict) copy(url = "") else this diff --git a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/SeriesControllerTest.kt b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/SeriesControllerTest.kt index 88b669d4fd..9e44c46c62 100644 --- a/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/SeriesControllerTest.kt +++ b/komga/src/test/kotlin/org/gotson/komga/interfaces/rest/SeriesControllerTest.kt @@ -570,5 +570,48 @@ class SeriesControllerTest( jsonPath("$.numberOfElements") { value(2) } } } + + @Test + @WithMockCustomUser(id = 2) + fun `given user when marking book as in progress then progress series return books count accordingly`() { + val series = makeSeries(name = "series", libraryId = library.id).let { series -> + seriesLifecycle.createSeries(series).also { created -> + val books = listOf( + makeBook("1.cbr", libraryId = library.id), + makeBook("2.cbr", libraryId = library.id), + makeBook("3.cbr", libraryId = library.id) + ) + seriesLifecycle.addBooks(created, books) + } + } + + bookRepository.findAll().forEach { book -> + mediaRepository.findById(book.id).let { + mediaRepository.update(it.copy( + status = Media.Status.READY, + pages = (1..10).map { BookPage("$it", "image/jpeg") } + )) + } + } + + val books = bookRepository.findAll().sortedBy { it.name } + + mockMvc.patch("/api/v1/books/${books.elementAt(0).id}/read-progress") { + contentType = MediaType.APPLICATION_JSON + content = """{"page": 5,"completed":false}""" + } + mockMvc.patch("/api/v1/books/${books.elementAt(1).id}/read-progress") { + contentType = MediaType.APPLICATION_JSON + content = """{"completed":true}""" + } + + mockMvc.get("/api/v1/series/${series.id}") + .andExpect { + status { isOk } + jsonPath("$.booksUnreadCount") { value(1) } + jsonPath("$.booksReadCount") { value(1) } + jsonPath("$.booksInProgressCount") { value(1) } + } + } } }