mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-04 03:14:40 -05:00
Expose unread and download count of Manga in category api (#227)
* #224 Created view for unread and download badges * #224 Basic test structure * Created test and cleaned up a bit * Move counts to MangaDataClass and delete MangaViewDataClass * Readded trailing space * Removed SQL view and calculate with joins now
This commit is contained in:
@@ -7,19 +7,15 @@ package suwayomi.tachidesk.manga.impl
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import org.jetbrains.exposed.sql.SortOrder
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.and
|
|
||||||
import org.jetbrains.exposed.sql.deleteWhere
|
|
||||||
import org.jetbrains.exposed.sql.insert
|
|
||||||
import org.jetbrains.exposed.sql.select
|
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
import org.jetbrains.exposed.sql.update
|
|
||||||
import suwayomi.tachidesk.manga.impl.Category.DEFAULT_CATEGORY_ID
|
import suwayomi.tachidesk.manga.impl.Category.DEFAULT_CATEGORY_ID
|
||||||
import suwayomi.tachidesk.manga.impl.util.lang.isEmpty
|
import suwayomi.tachidesk.manga.impl.util.lang.isEmpty
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass
|
||||||
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
|
import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass
|
||||||
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
|
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
||||||
import suwayomi.tachidesk.manga.model.table.MangaTable
|
import suwayomi.tachidesk.manga.model.table.MangaTable
|
||||||
import suwayomi.tachidesk.manga.model.table.toDataClass
|
import suwayomi.tachidesk.manga.model.table.toDataClass
|
||||||
|
|
||||||
@@ -56,17 +52,38 @@ object CategoryManga {
|
|||||||
* list of mangas that belong to a category
|
* list of mangas that belong to a category
|
||||||
*/
|
*/
|
||||||
fun getCategoryMangaList(categoryId: Int): List<MangaDataClass> {
|
fun getCategoryMangaList(categoryId: Int): List<MangaDataClass> {
|
||||||
|
val unreadExpression = wrapAsExpression<Long>(
|
||||||
|
ChapterTable
|
||||||
|
.slice(ChapterTable.id.count())
|
||||||
|
.select { (MangaTable.id eq ChapterTable.manga) and (ChapterTable.isRead eq false) }
|
||||||
|
)
|
||||||
|
val downloadExpression = wrapAsExpression<Long>(
|
||||||
|
ChapterTable
|
||||||
|
.slice(ChapterTable.id.count())
|
||||||
|
.select { (MangaTable.id eq ChapterTable.manga) and (ChapterTable.isDownloaded eq true) }
|
||||||
|
)
|
||||||
|
|
||||||
|
val selectedColumns = MangaTable.columns + unreadExpression + downloadExpression
|
||||||
|
val transform: (ResultRow) -> MangaDataClass = {
|
||||||
|
val dataClass = MangaTable.toDataClass(it)
|
||||||
|
dataClass.unread_count = it[unreadExpression]?.toInt()
|
||||||
|
dataClass.download_count = it[downloadExpression]?.toInt()
|
||||||
|
dataClass
|
||||||
|
}
|
||||||
|
|
||||||
if (categoryId == DEFAULT_CATEGORY_ID)
|
if (categoryId == DEFAULT_CATEGORY_ID)
|
||||||
return transaction {
|
return transaction {
|
||||||
MangaTable.select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) }.map {
|
MangaTable
|
||||||
MangaTable.toDataClass(it)
|
.slice(selectedColumns)
|
||||||
}
|
.select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) }
|
||||||
|
.map(transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
return transaction {
|
return transaction {
|
||||||
CategoryMangaTable.innerJoin(MangaTable).select { CategoryMangaTable.category eq categoryId }.map {
|
CategoryMangaTable.innerJoin(MangaTable)
|
||||||
MangaTable.toDataClass(it)
|
.slice(selectedColumns)
|
||||||
}
|
.select { CategoryMangaTable.category eq categoryId }
|
||||||
|
.map(transform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ data class MangaDataClass(
|
|||||||
val realUrl: String? = null,
|
val realUrl: String? = null,
|
||||||
|
|
||||||
val freshData: Boolean = false,
|
val freshData: Boolean = false,
|
||||||
|
var unread_count: Int? = null,
|
||||||
|
var download_count: Int? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
data class PagedMangaListDataClass(
|
data class PagedMangaListDataClass(
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package suwayomi.tachidesk.manga.impl
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Contributors to the Suwayomi project
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.sql.batchInsert
|
||||||
|
import org.jetbrains.exposed.sql.deleteAll
|
||||||
|
import org.jetbrains.exposed.sql.insertAndGetId
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
import org.junit.jupiter.api.AfterEach
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import suwayomi.BASE_PATH
|
||||||
|
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.CategoryTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.ChapterTable
|
||||||
|
import suwayomi.tachidesk.manga.model.table.ChapterTable.isRead
|
||||||
|
import suwayomi.tachidesk.manga.model.table.ChapterTable.manga
|
||||||
|
import suwayomi.tachidesk.manga.model.table.ChapterTable.name
|
||||||
|
import suwayomi.tachidesk.manga.model.table.ChapterTable.sourceOrder
|
||||||
|
import suwayomi.tachidesk.manga.model.table.ChapterTable.url
|
||||||
|
import suwayomi.tachidesk.manga.model.table.MangaTable
|
||||||
|
import suwayomi.tachidesk.server.applicationSetup
|
||||||
|
import xyz.nulldev.ts.config.CONFIG_PREFIX
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class CategoryMangaTest {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setUp() {
|
||||||
|
val dataRoot = File(BASE_PATH).absolutePath
|
||||||
|
System.setProperty("$CONFIG_PREFIX.server.rootDir", dataRoot)
|
||||||
|
applicationSetup()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getCategoryMangaList() {
|
||||||
|
val emptyCats = CategoryManga.getCategoryMangaList(0).size
|
||||||
|
assertEquals(0, emptyCats, "Default category should be empty at start")
|
||||||
|
val mangaId = createManga("Psyren")
|
||||||
|
createChapters(mangaId, 10, true)
|
||||||
|
assertEquals(1, CategoryManga.getCategoryMangaList(0).size, "Default category should have one member")
|
||||||
|
assertEquals(
|
||||||
|
0, CategoryManga.getCategoryMangaList(0)[0].unread_count,
|
||||||
|
"Manga should not have any unread chapters"
|
||||||
|
)
|
||||||
|
createChapters(mangaId, 10, false)
|
||||||
|
assertEquals(
|
||||||
|
10, CategoryManga.getCategoryMangaList(0)[0].unread_count,
|
||||||
|
"Manga should have unread chapters"
|
||||||
|
)
|
||||||
|
|
||||||
|
Category.createCategory("Old") // category id 1
|
||||||
|
assertEquals(
|
||||||
|
0,
|
||||||
|
CategoryManga.getCategoryMangaList(1).size,
|
||||||
|
"Newly created category shouldn't have any Mangas"
|
||||||
|
)
|
||||||
|
CategoryManga.addMangaToCategory(mangaId, 1)
|
||||||
|
assertEquals(
|
||||||
|
1, CategoryManga.getCategoryMangaList(1).size,
|
||||||
|
"Manga should been moved"
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
10, CategoryManga.getCategoryMangaList(1)[0].unread_count,
|
||||||
|
"Manga should keep it's unread count in moved category"
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
0, CategoryManga.getCategoryMangaList(0).size,
|
||||||
|
"Manga shouldn't be member of default category after moving"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createManga(
|
||||||
|
_title: String
|
||||||
|
): Int {
|
||||||
|
return transaction {
|
||||||
|
MangaTable.insertAndGetId {
|
||||||
|
it[title] = _title
|
||||||
|
it[url] = _title
|
||||||
|
it[sourceReference] = 1
|
||||||
|
it[defaultCategory] = true
|
||||||
|
it[inLibrary] = true
|
||||||
|
}.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createChapters(
|
||||||
|
mangaId: Int,
|
||||||
|
amount: Int,
|
||||||
|
read: Boolean
|
||||||
|
) {
|
||||||
|
val list = listOf((0 until amount)).flatten().map { 1 }
|
||||||
|
transaction {
|
||||||
|
ChapterTable
|
||||||
|
.batchInsert(list) {
|
||||||
|
this[url] = "$it"
|
||||||
|
this[name] = "$it"
|
||||||
|
this[sourceOrder] = it
|
||||||
|
this[isRead] = read
|
||||||
|
this[manga] = mangaId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
internal fun tearDown() {
|
||||||
|
transaction {
|
||||||
|
ChapterTable.deleteAll()
|
||||||
|
CategoryMangaTable.deleteAll()
|
||||||
|
MangaTable.deleteAll()
|
||||||
|
CategoryTable.deleteAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user