Fix filtering Genre.

This commit is contained in:
Thomas Andres Gomez 2022-07-06 15:25:43 +02:00
parent 5a70a393b5
commit 8b0cebbd75
14 changed files with 125 additions and 35 deletions

View file

@ -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')"
] ]
} }
} }

View file

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

View file

@ -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,
) { ) {

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +0,0 @@
package com.pixelized.biblib.network.factory
object Factory {
const val FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
}

View file

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

View file

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

View file

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

View file

@ -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'"

View file

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

View file

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