diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt index 67b8153e..cc79e293 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/CategoryManga.kt @@ -7,19 +7,15 @@ package suwayomi.tachidesk.manga.impl * 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.SortOrder -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.* 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.util.lang.isEmpty import suwayomi.tachidesk.manga.model.dataclass.CategoryDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass 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.MangaTable import suwayomi.tachidesk.manga.model.table.toDataClass @@ -56,17 +52,38 @@ object CategoryManga { * list of mangas that belong to a category */ fun getCategoryMangaList(categoryId: Int): List { + val unreadExpression = wrapAsExpression( + ChapterTable + .slice(ChapterTable.id.count()) + .select { (MangaTable.id eq ChapterTable.manga) and (ChapterTable.isRead eq false) } + ) + val downloadExpression = wrapAsExpression( + 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) return transaction { - MangaTable.select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) }.map { - MangaTable.toDataClass(it) - } + MangaTable + .slice(selectedColumns) + .select { (MangaTable.inLibrary eq true) and (MangaTable.defaultCategory eq true) } + .map(transform) } return transaction { - CategoryMangaTable.innerJoin(MangaTable).select { CategoryMangaTable.category eq categoryId }.map { - MangaTable.toDataClass(it) - } + CategoryMangaTable.innerJoin(MangaTable) + .slice(selectedColumns) + .select { CategoryMangaTable.category eq categoryId } + .map(transform) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt index 0b3353ff..02275fe9 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/model/dataclass/MangaDataClass.kt @@ -35,6 +35,8 @@ data class MangaDataClass( val realUrl: String? = null, val freshData: Boolean = false, + var unread_count: Int? = null, + var download_count: Int? = null ) data class PagedMangaListDataClass( diff --git a/server/src/test/kotlin/suwayomi/tachidesk/manga/impl/CategoryMangaTest.kt b/server/src/test/kotlin/suwayomi/tachidesk/manga/impl/CategoryMangaTest.kt new file mode 100644 index 00000000..33cd4f30 --- /dev/null +++ b/server/src/test/kotlin/suwayomi/tachidesk/manga/impl/CategoryMangaTest.kt @@ -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() + } + } +}