Fix filtering Genre.
This commit is contained in:
parent
5a70a393b5
commit
8b0cebbd75
14 changed files with 125 additions and 35 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"identityHash": "30ba58aae4cd6855b019ac599eb57bdb",
|
"identityHash": "eb9a9ac9ba58cccd34d5fa89bcd4151c",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "AUTHOR",
|
"tableName": "AUTHOR",
|
||||||
|
|
@ -118,12 +118,12 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "GENRE",
|
"tableName": "GENRE",
|
||||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GENRE_ID` TEXT NOT NULL, `GENRE_NAME` TEXT NOT NULL, PRIMARY KEY(`GENRE_ID`))",
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GENRE_ID` INTEGER NOT NULL, `GENRE_NAME` TEXT NOT NULL, PRIMARY KEY(`GENRE_ID`))",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
"columnName": "GENRE_ID",
|
"columnName": "GENRE_ID",
|
||||||
"affinity": "TEXT",
|
"affinity": "INTEGER",
|
||||||
"notNull": true
|
"notNull": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -245,7 +245,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "BookGenreCrossRef",
|
"tableName": "BookGenreCrossRef",
|
||||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`BOOK_ID` INTEGER NOT NULL, `GENRE_ID` TEXT NOT NULL, PRIMARY KEY(`BOOK_ID`, `GENRE_ID`))",
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`BOOK_ID` INTEGER NOT NULL, `GENRE_ID` INTEGER NOT NULL, PRIMARY KEY(`BOOK_ID`, `GENRE_ID`))",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "bookId",
|
"fieldPath": "bookId",
|
||||||
|
|
@ -256,7 +256,7 @@
|
||||||
{
|
{
|
||||||
"fieldPath": "genreId",
|
"fieldPath": "genreId",
|
||||||
"columnName": "GENRE_ID",
|
"columnName": "GENRE_ID",
|
||||||
"affinity": "TEXT",
|
"affinity": "INTEGER",
|
||||||
"notNull": true
|
"notNull": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
@ -284,7 +284,7 @@
|
||||||
"views": [],
|
"views": [],
|
||||||
"setupQueries": [
|
"setupQueries": [
|
||||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '30ba58aae4cd6855b019ac599eb57bdb')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'eb9a9ac9ba58cccd34d5fa89bcd4151c')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,5 +10,5 @@ data class BookGenreCrossRef(
|
||||||
@ColumnInfo(name = BookDbo.ID)
|
@ColumnInfo(name = BookDbo.ID)
|
||||||
val bookId: Int,
|
val bookId: Int,
|
||||||
@ColumnInfo(name = GenreDbo.ID, index = true)
|
@ColumnInfo(name = GenreDbo.ID, index = true)
|
||||||
val genreId: String
|
val genreId: Int,
|
||||||
)
|
)
|
||||||
|
|
@ -8,7 +8,7 @@ import androidx.room.PrimaryKey
|
||||||
data class GenreDbo(
|
data class GenreDbo(
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
@ColumnInfo(name = ID)
|
@ColumnInfo(name = ID)
|
||||||
val id: String,
|
val id: Int,
|
||||||
@ColumnInfo(name = NAME)
|
@ColumnInfo(name = NAME)
|
||||||
val name: String,
|
val name: String,
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package com.pixelized.biblib.model.book
|
package com.pixelized.biblib.model.book
|
||||||
|
|
||||||
data class Genre(
|
data class Genre(
|
||||||
val id: String,
|
val id: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
)
|
)
|
||||||
|
|
@ -24,6 +24,9 @@ interface IBibLibWebServiceAPI {
|
||||||
@GET("/api/series")
|
@GET("/api/series")
|
||||||
suspend fun series(): SeriesListResponse
|
suspend fun series(): SeriesListResponse
|
||||||
|
|
||||||
|
@GET("/api/tag")
|
||||||
|
suspend fun genre(): GenreListResponse
|
||||||
|
|
||||||
@GET("/api/book/{id}")
|
@GET("/api/book/{id}")
|
||||||
suspend fun detail(@Path("id") bookId: Int): BookDetailResponse
|
suspend fun detail(@Path("id") bookId: Int): BookDetailResponse
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.pixelized.biblib.network.data.response
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
data class GenreListResponse(
|
||||||
|
@SerializedName("lastUpdated")
|
||||||
|
val lastUpdated: String?,
|
||||||
|
@SerializedName("data")
|
||||||
|
val genres: List<Genres>?,
|
||||||
|
) : ErrorResponse() {
|
||||||
|
|
||||||
|
data class Genres(
|
||||||
|
@SerializedName("tag_id")
|
||||||
|
val tag_id: Int?,
|
||||||
|
@SerializedName("tag_name")
|
||||||
|
val tag_name: String?,
|
||||||
|
@SerializedName("books")
|
||||||
|
val books: List<Books>?,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Books(
|
||||||
|
@SerializedName("book_id")
|
||||||
|
val book_id: Int?,
|
||||||
|
@SerializedName("book_title")
|
||||||
|
val book_title: String?,
|
||||||
|
@SerializedName("book_has_cover")
|
||||||
|
val book_has_cover: Int?,
|
||||||
|
@SerializedName("book_date")
|
||||||
|
val book_date: String?,
|
||||||
|
@SerializedName("book_series_index")
|
||||||
|
val book_series_index: Float?,
|
||||||
|
@SerializedName("last_modified")
|
||||||
|
val last_modified: String?,
|
||||||
|
@SerializedName("author_name")
|
||||||
|
val author_name: List<String>?,
|
||||||
|
@SerializedName("lang_id")
|
||||||
|
val lang_id: Int?,
|
||||||
|
@SerializedName("lang_code")
|
||||||
|
val lang_code: String?,
|
||||||
|
@SerializedName("rating")
|
||||||
|
val rating: String?,
|
||||||
|
@SerializedName("series_id")
|
||||||
|
val series_id: Int?,
|
||||||
|
@SerializedName("series_name")
|
||||||
|
val series_name: String?,
|
||||||
|
@SerializedName("series_sort")
|
||||||
|
val series_sort: String?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -3,23 +3,22 @@ package com.pixelized.biblib.network.factory
|
||||||
import com.pixelized.biblib.model.book.*
|
import com.pixelized.biblib.model.book.*
|
||||||
import com.pixelized.biblib.network.data.response.BookDetailResponse
|
import com.pixelized.biblib.network.data.response.BookDetailResponse
|
||||||
import com.pixelized.biblib.network.data.response.BookListResponse
|
import com.pixelized.biblib.network.data.response.BookListResponse
|
||||||
import com.pixelized.biblib.utils.exception.MandatoryFieldMissingException
|
import com.pixelized.biblib.utils.exception.missingField
|
||||||
import com.pixelized.biblib.utils.extention.toBoolean
|
import com.pixelized.biblib.utils.extention.toBoolean
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.collections.HashMap
|
|
||||||
|
|
||||||
class BookFactory {
|
class BookFactory {
|
||||||
private val parser get() = SimpleDateFormat(Factory.FORMAT, Locale.getDefault())
|
private val parser get() = SimpleDateFormat(BIBLIB_API_DATE_FORMAT, Locale.getDefault())
|
||||||
|
|
||||||
fun fromListResponseToBook(
|
fun fromListResponseToBook(
|
||||||
response: BookListResponse.Book,
|
response: BookListResponse.Book,
|
||||||
seriesHash: HashMap<Int, Series>,
|
seriesHash: Map<Int, Series>,
|
||||||
|
genreHash: Map<Int, List<Genre>>,
|
||||||
isNew: Boolean = false,
|
isNew: Boolean = false,
|
||||||
newOrder: Int? = null
|
newOrder: Int? = null
|
||||||
): Book {
|
): Book {
|
||||||
fun error(name: String) =
|
fun error(name: String) = missingField("#fromListResponseToBook()", name, response)
|
||||||
MandatoryFieldMissingException("#fromListResponseToBook()", name, response)
|
|
||||||
|
|
||||||
val id: Int? = response.book_id
|
val id: Int? = response.book_id
|
||||||
val title: String? = response.book_title
|
val title: String? = response.book_title
|
||||||
|
|
@ -80,14 +79,14 @@ class BookFactory {
|
||||||
series = seriesHash[id],
|
series = seriesHash[id],
|
||||||
language = language,
|
language = language,
|
||||||
rating = rating,
|
rating = rating,
|
||||||
|
genre = genreHash[id],
|
||||||
isNew = isNew,
|
isNew = isNew,
|
||||||
newOrder = newOrder,
|
newOrder = newOrder,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromDetailResponseToBook(response: BookDetailResponse, isNew: Boolean = false): Book {
|
fun fromDetailResponseToBook(response: BookDetailResponse, isNew: Boolean = false): Book {
|
||||||
fun error(name: String) =
|
fun error(name: String) = missingField("#fromDetailResponseToBook()", name, response)
|
||||||
MandatoryFieldMissingException("#fromDetailResponseToBook()", name, response)
|
|
||||||
|
|
||||||
val id: Int? = response.book_id
|
val id: Int? = response.book_id
|
||||||
val title: String? = response.book_title
|
val title: String? = response.book_title
|
||||||
|
|
@ -129,7 +128,7 @@ class BookFactory {
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val tagId: List<String>? = response.tag_id
|
val tagId: List<Int>? = response.tag_id?.mapNotNull { it.toIntOrNull() }
|
||||||
val tagName: List<String>? = response.tag_name
|
val tagName: List<String>? = response.tag_name
|
||||||
val tagIdSize: Int? = response.tag_id?.size
|
val tagIdSize: Int? = response.tag_id?.size
|
||||||
val tagNameSize: Int? = response.tag_name?.size
|
val tagNameSize: Int? = response.tag_name?.size
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
package com.pixelized.biblib.network.factory
|
|
||||||
|
|
||||||
object Factory {
|
|
||||||
const val FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.pixelized.biblib.network.factory
|
||||||
|
|
||||||
|
import com.pixelized.biblib.model.book.Genre
|
||||||
|
import com.pixelized.biblib.network.data.response.GenreListResponse
|
||||||
|
import com.pixelized.biblib.utils.exception.missingField
|
||||||
|
|
||||||
|
class GenreFactory {
|
||||||
|
|
||||||
|
fun fromListResponseToGenreHash(
|
||||||
|
response: GenreListResponse,
|
||||||
|
): Map<Int, List<Genre>> {
|
||||||
|
fun error(name: String) = missingField("#fromListResponseToGenreHash()", name, response)
|
||||||
|
|
||||||
|
val hash = mutableMapOf<Int, MutableList<Genre>>()
|
||||||
|
|
||||||
|
response.genres?.forEach { data ->
|
||||||
|
val genre = Genre(
|
||||||
|
id = data.tag_id ?: throw error("id"),
|
||||||
|
name = data.tag_name ?: throw error("name"),
|
||||||
|
)
|
||||||
|
data.books?.forEach { book ->
|
||||||
|
book.book_id?.let { id ->
|
||||||
|
hash.getOrPut(id) { mutableListOf() }.add(genre)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,18 +2,16 @@ package com.pixelized.biblib.network.factory
|
||||||
|
|
||||||
import com.pixelized.biblib.model.book.Series
|
import com.pixelized.biblib.model.book.Series
|
||||||
import com.pixelized.biblib.network.data.response.SeriesListResponse
|
import com.pixelized.biblib.network.data.response.SeriesListResponse
|
||||||
import com.pixelized.biblib.utils.exception.MandatoryFieldMissingException
|
import com.pixelized.biblib.utils.exception.missingField
|
||||||
|
|
||||||
class SeriesFactory {
|
class SeriesFactory {
|
||||||
|
|
||||||
fun fromListResponseToSeriesHash(
|
fun fromListResponseToSeriesHash(
|
||||||
response: SeriesListResponse,
|
response: SeriesListResponse,
|
||||||
): HashMap<Int, Series> {
|
): Map<Int, Series> {
|
||||||
|
fun error(name: String) = missingField("#fromListResponseToSeriesHash()", name, response)
|
||||||
|
|
||||||
fun error(name: String) =
|
val hash = mutableMapOf<Int, Series>()
|
||||||
MandatoryFieldMissingException("#fromListResponseToSeriesHash()", name, response)
|
|
||||||
|
|
||||||
val hash = hashMapOf<Int, Series>()
|
|
||||||
|
|
||||||
response.series?.forEachIndexed { index, data ->
|
response.series?.forEachIndexed { index, data ->
|
||||||
// build the Series items
|
// build the Series items
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,17 @@ package com.pixelized.biblib.network.factory
|
||||||
import com.pixelized.biblib.model.user.DownloadedBooks
|
import com.pixelized.biblib.model.user.DownloadedBooks
|
||||||
import com.pixelized.biblib.model.user.User
|
import com.pixelized.biblib.model.user.User
|
||||||
import com.pixelized.biblib.network.data.response.UserResponse
|
import com.pixelized.biblib.network.data.response.UserResponse
|
||||||
import com.pixelized.biblib.utils.exception.MandatoryFieldMissingException
|
import com.pixelized.biblib.utils.exception.missingField
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class UserFactory {
|
class UserFactory {
|
||||||
private val parser get() = SimpleDateFormat(Factory.FORMAT, Locale.getDefault())
|
private val parser get() = SimpleDateFormat(BIBLIB_API_DATE_FORMAT, Locale.getDefault())
|
||||||
|
|
||||||
fun fromUserResponseToUser(
|
fun fromUserResponseToUser(
|
||||||
response: UserResponse
|
response: UserResponse
|
||||||
): User {
|
): User {
|
||||||
fun error(name: String) =
|
fun error(name: String) = missingField("#fromUserResponseToUser()", name, response)
|
||||||
MandatoryFieldMissingException("#fromUserResponseToUser()", name, response)
|
|
||||||
|
|
||||||
val id = response.data?.id
|
val id = response.data?.id
|
||||||
val created = response.data?.created?.let { parser.parse(it) }
|
val created = response.data?.created?.let { parser.parse(it) }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
package com.pixelized.biblib.network.factory
|
||||||
|
|
||||||
|
const val BIBLIB_API_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
|
||||||
|
|
@ -3,8 +3,10 @@ package com.pixelized.biblib.repository.book
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.pixelized.biblib.network.client.IBibLibClient
|
import com.pixelized.biblib.network.client.IBibLibClient
|
||||||
import com.pixelized.biblib.network.data.response.BookListResponse
|
import com.pixelized.biblib.network.data.response.BookListResponse
|
||||||
|
import com.pixelized.biblib.network.data.response.GenreListResponse
|
||||||
import com.pixelized.biblib.network.data.response.SeriesListResponse
|
import com.pixelized.biblib.network.data.response.SeriesListResponse
|
||||||
import com.pixelized.biblib.network.factory.BookFactory
|
import com.pixelized.biblib.network.factory.BookFactory
|
||||||
|
import com.pixelized.biblib.network.factory.GenreFactory
|
||||||
import com.pixelized.biblib.network.factory.SeriesFactory
|
import com.pixelized.biblib.network.factory.SeriesFactory
|
||||||
import com.pixelized.biblib.repository.apiCache.IAPICacheRepository
|
import com.pixelized.biblib.repository.apiCache.IAPICacheRepository
|
||||||
import com.pixelized.biblib.utils.exception.BookFetchException
|
import com.pixelized.biblib.utils.exception.BookFetchException
|
||||||
|
|
@ -63,9 +65,11 @@ suspend fun loadAllBooks(
|
||||||
repository: IBookRepository,
|
repository: IBookRepository,
|
||||||
bookFactory: BookFactory = BookFactory(),
|
bookFactory: BookFactory = BookFactory(),
|
||||||
seriesFactory: SeriesFactory = SeriesFactory(),
|
seriesFactory: SeriesFactory = SeriesFactory(),
|
||||||
|
genresFactory: GenreFactory = GenreFactory(),
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val seriesResponse: SeriesListResponse = client.service.series()
|
val seriesResponse: SeriesListResponse = client.service.series()
|
||||||
val bookResponse: BookListResponse = client.service.list()
|
val bookResponse: BookListResponse = client.service.list()
|
||||||
|
val genreResponse: GenreListResponse = client.service.genre()
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
bookResponse.isError -> {
|
bookResponse.isError -> {
|
||||||
|
|
@ -76,11 +80,17 @@ suspend fun loadAllBooks(
|
||||||
Log.e("loadAllBooks", seriesResponse.message ?: "")
|
Log.e("loadAllBooks", seriesResponse.message ?: "")
|
||||||
throw BookFetchException(seriesResponse.message)
|
throw BookFetchException(seriesResponse.message)
|
||||||
}
|
}
|
||||||
|
genreResponse.isError -> {
|
||||||
|
Log.e("loadAllBooks", genreResponse.message ?: "")
|
||||||
|
throw BookFetchException(genreResponse.message)
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// prepare a list of new book ids.
|
// prepare a list of new book ids.
|
||||||
val newIds = cache.new?.data?.map { it.book_id } ?: listOf()
|
val newIds = cache.new?.data?.map { it.book_id } ?: listOf()
|
||||||
// prepare a hash of series by books ids.
|
// prepare a hash of series by books ids.
|
||||||
val series = seriesFactory.fromListResponseToSeriesHash(seriesResponse)
|
val series = seriesFactory.fromListResponseToSeriesHash(response = seriesResponse)
|
||||||
|
// prepare a hash of genre by books ids.
|
||||||
|
val genres = genresFactory.fromListResponseToGenreHash(response = genreResponse)
|
||||||
// parse books.
|
// parse books.
|
||||||
val books = bookResponse.data?.map { dto ->
|
val books = bookResponse.data?.map { dto ->
|
||||||
val isNew = newIds.contains(dto.book_id)
|
val isNew = newIds.contains(dto.book_id)
|
||||||
|
|
@ -88,6 +98,7 @@ suspend fun loadAllBooks(
|
||||||
return@map bookFactory.fromListResponseToBook(
|
return@map bookFactory.fromListResponseToBook(
|
||||||
response = dto,
|
response = dto,
|
||||||
seriesHash = series,
|
seriesHash = series,
|
||||||
|
genreHash = genres,
|
||||||
isNew = isNew,
|
isNew = isNew,
|
||||||
newOrder = index
|
newOrder = index
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
package com.pixelized.biblib.utils.exception
|
package com.pixelized.biblib.utils.exception
|
||||||
|
|
||||||
class MandatoryFieldMissingException(method: String, name: String, json: Any) :
|
private class MandatoryFieldMissingException(method: String, name: String, json: Any) :
|
||||||
|
RuntimeException("Parse exception: Mandatory field:'$name' is missing from:'$method', with data:$json")
|
||||||
|
|
||||||
|
fun missingField(method: String, name: String, json: Any) =
|
||||||
RuntimeException("Parse exception: Mandatory field:'$name' is missing from:'$method', with data:$json")
|
RuntimeException("Parse exception: Mandatory field:'$name' is missing from:'$method', with data:$json")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue