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:
Sascha Hahne
2021-10-25 12:11:07 +02:00
committed by GitHub
parent d12974702a
commit 0057b35a0a
3 changed files with 153 additions and 12 deletions

View File

@@ -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<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)
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)
}
}

View File

@@ -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(

View File

@@ -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()
}
}
}