diff --git a/app/schemas/com.pixelized.biblib.database.BibLibDatabase/1.json b/app/schemas/com.pixelized.biblib.database.BibLibDatabase/1.json index 96a3267..8326e57 100644 --- a/app/schemas/com.pixelized.biblib.database.BibLibDatabase/1.json +++ b/app/schemas/com.pixelized.biblib.database.BibLibDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "30ba58aae4cd6855b019ac599eb57bdb", + "identityHash": "eb9a9ac9ba58cccd34d5fa89bcd4151c", "entities": [ { "tableName": "AUTHOR", @@ -118,12 +118,12 @@ }, { "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": [ { "fieldPath": "id", "columnName": "GENRE_ID", - "affinity": "TEXT", + "affinity": "INTEGER", "notNull": true }, { @@ -245,7 +245,7 @@ }, { "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": [ { "fieldPath": "bookId", @@ -256,7 +256,7 @@ { "fieldPath": "genreId", "columnName": "GENRE_ID", - "affinity": "TEXT", + "affinity": "INTEGER", "notNull": true } ], @@ -284,7 +284,7 @@ "views": [], "setupQueries": [ "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')" ] } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/database/crossref/BookGenreCrossRef.kt b/app/src/main/java/com/pixelized/biblib/database/crossref/BookGenreCrossRef.kt index 6db7065..7ae574b 100644 --- a/app/src/main/java/com/pixelized/biblib/database/crossref/BookGenreCrossRef.kt +++ b/app/src/main/java/com/pixelized/biblib/database/crossref/BookGenreCrossRef.kt @@ -10,5 +10,5 @@ data class BookGenreCrossRef( @ColumnInfo(name = BookDbo.ID) val bookId: Int, @ColumnInfo(name = GenreDbo.ID, index = true) - val genreId: String + val genreId: Int, ) \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/database/data/GenreDbo.kt b/app/src/main/java/com/pixelized/biblib/database/data/GenreDbo.kt index 2ec091f..42d1283 100644 --- a/app/src/main/java/com/pixelized/biblib/database/data/GenreDbo.kt +++ b/app/src/main/java/com/pixelized/biblib/database/data/GenreDbo.kt @@ -8,7 +8,7 @@ import androidx.room.PrimaryKey data class GenreDbo( @PrimaryKey @ColumnInfo(name = ID) - val id: String, + val id: Int, @ColumnInfo(name = NAME) val name: String, ) { diff --git a/app/src/main/java/com/pixelized/biblib/model/book/Genre.kt b/app/src/main/java/com/pixelized/biblib/model/book/Genre.kt index 8b9ed55..9501b79 100644 --- a/app/src/main/java/com/pixelized/biblib/model/book/Genre.kt +++ b/app/src/main/java/com/pixelized/biblib/model/book/Genre.kt @@ -1,6 +1,6 @@ package com.pixelized.biblib.model.book data class Genre( - val id: String, + val id: Int, val name: String, ) \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/network/client/IBibLibWebServiceAPI.kt b/app/src/main/java/com/pixelized/biblib/network/client/IBibLibWebServiceAPI.kt index ad12895..6af7104 100644 --- a/app/src/main/java/com/pixelized/biblib/network/client/IBibLibWebServiceAPI.kt +++ b/app/src/main/java/com/pixelized/biblib/network/client/IBibLibWebServiceAPI.kt @@ -24,6 +24,9 @@ interface IBibLibWebServiceAPI { @GET("/api/series") suspend fun series(): SeriesListResponse + @GET("/api/tag") + suspend fun genre(): GenreListResponse + @GET("/api/book/{id}") suspend fun detail(@Path("id") bookId: Int): BookDetailResponse diff --git a/app/src/main/java/com/pixelized/biblib/network/data/response/GenreListResponse.kt b/app/src/main/java/com/pixelized/biblib/network/data/response/GenreListResponse.kt new file mode 100644 index 0000000..0550902 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/network/data/response/GenreListResponse.kt @@ -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?, +) : ErrorResponse() { + + data class Genres( + @SerializedName("tag_id") + val tag_id: Int?, + @SerializedName("tag_name") + val tag_name: String?, + @SerializedName("books") + val books: List?, + ) + + 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?, + @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?, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/network/factory/BookFactory.kt b/app/src/main/java/com/pixelized/biblib/network/factory/BookFactory.kt index 0536dfc..2f7c0e7 100644 --- a/app/src/main/java/com/pixelized/biblib/network/factory/BookFactory.kt +++ b/app/src/main/java/com/pixelized/biblib/network/factory/BookFactory.kt @@ -3,23 +3,22 @@ package com.pixelized.biblib.network.factory import com.pixelized.biblib.model.book.* import com.pixelized.biblib.network.data.response.BookDetailResponse 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 java.text.SimpleDateFormat import java.util.* -import kotlin.collections.HashMap class BookFactory { - private val parser get() = SimpleDateFormat(Factory.FORMAT, Locale.getDefault()) + private val parser get() = SimpleDateFormat(BIBLIB_API_DATE_FORMAT, Locale.getDefault()) fun fromListResponseToBook( response: BookListResponse.Book, - seriesHash: HashMap, + seriesHash: Map, + genreHash: Map>, isNew: Boolean = false, newOrder: Int? = null ): Book { - fun error(name: String) = - MandatoryFieldMissingException("#fromListResponseToBook()", name, response) + fun error(name: String) = missingField("#fromListResponseToBook()", name, response) val id: Int? = response.book_id val title: String? = response.book_title @@ -80,14 +79,14 @@ class BookFactory { series = seriesHash[id], language = language, rating = rating, + genre = genreHash[id], isNew = isNew, newOrder = newOrder, ) } fun fromDetailResponseToBook(response: BookDetailResponse, isNew: Boolean = false): Book { - fun error(name: String) = - MandatoryFieldMissingException("#fromDetailResponseToBook()", name, response) + fun error(name: String) = missingField("#fromDetailResponseToBook()", name, response) val id: Int? = response.book_id val title: String? = response.book_title @@ -129,7 +128,7 @@ class BookFactory { } else { null } - val tagId: List? = response.tag_id + val tagId: List? = response.tag_id?.mapNotNull { it.toIntOrNull() } val tagName: List? = response.tag_name val tagIdSize: Int? = response.tag_id?.size val tagNameSize: Int? = response.tag_name?.size diff --git a/app/src/main/java/com/pixelized/biblib/network/factory/Factory.kt b/app/src/main/java/com/pixelized/biblib/network/factory/Factory.kt deleted file mode 100644 index 469f33f..0000000 --- a/app/src/main/java/com/pixelized/biblib/network/factory/Factory.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.pixelized.biblib.network.factory - -object Factory { - const val FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" -} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/network/factory/GenreFactory.kt b/app/src/main/java/com/pixelized/biblib/network/factory/GenreFactory.kt new file mode 100644 index 0000000..ae50e56 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/network/factory/GenreFactory.kt @@ -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> { + fun error(name: String) = missingField("#fromListResponseToGenreHash()", name, response) + + val hash = mutableMapOf>() + + 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/network/factory/SeriesFactory.kt b/app/src/main/java/com/pixelized/biblib/network/factory/SeriesFactory.kt index ee4e044..e1dd4d5 100644 --- a/app/src/main/java/com/pixelized/biblib/network/factory/SeriesFactory.kt +++ b/app/src/main/java/com/pixelized/biblib/network/factory/SeriesFactory.kt @@ -2,18 +2,16 @@ package com.pixelized.biblib.network.factory import com.pixelized.biblib.model.book.Series import com.pixelized.biblib.network.data.response.SeriesListResponse -import com.pixelized.biblib.utils.exception.MandatoryFieldMissingException +import com.pixelized.biblib.utils.exception.missingField class SeriesFactory { fun fromListResponseToSeriesHash( response: SeriesListResponse, - ): HashMap { + ): Map { + fun error(name: String) = missingField("#fromListResponseToSeriesHash()", name, response) - fun error(name: String) = - MandatoryFieldMissingException("#fromListResponseToSeriesHash()", name, response) - - val hash = hashMapOf() + val hash = mutableMapOf() response.series?.forEachIndexed { index, data -> // build the Series items diff --git a/app/src/main/java/com/pixelized/biblib/network/factory/UserFactory.kt b/app/src/main/java/com/pixelized/biblib/network/factory/UserFactory.kt index bcadf47..082219e 100644 --- a/app/src/main/java/com/pixelized/biblib/network/factory/UserFactory.kt +++ b/app/src/main/java/com/pixelized/biblib/network/factory/UserFactory.kt @@ -3,18 +3,17 @@ package com.pixelized.biblib.network.factory import com.pixelized.biblib.model.user.DownloadedBooks import com.pixelized.biblib.model.user.User 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.util.* class UserFactory { - private val parser get() = SimpleDateFormat(Factory.FORMAT, Locale.getDefault()) + private val parser get() = SimpleDateFormat(BIBLIB_API_DATE_FORMAT, Locale.getDefault()) fun fromUserResponseToUser( response: UserResponse ): User { - fun error(name: String) = - MandatoryFieldMissingException("#fromUserResponseToUser()", name, response) + fun error(name: String) = missingField("#fromUserResponseToUser()", name, response) val id = response.data?.id val created = response.data?.created?.let { parser.parse(it) } diff --git a/app/src/main/java/com/pixelized/biblib/network/factory/_Constant.kt b/app/src/main/java/com/pixelized/biblib/network/factory/_Constant.kt new file mode 100644 index 0000000..0396388 --- /dev/null +++ b/app/src/main/java/com/pixelized/biblib/network/factory/_Constant.kt @@ -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'" \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/biblib/repository/book/BookUtils.kt b/app/src/main/java/com/pixelized/biblib/repository/book/BookUtils.kt index 38c6464..949c7fb 100644 --- a/app/src/main/java/com/pixelized/biblib/repository/book/BookUtils.kt +++ b/app/src/main/java/com/pixelized/biblib/repository/book/BookUtils.kt @@ -3,8 +3,10 @@ package com.pixelized.biblib.repository.book import android.util.Log import com.pixelized.biblib.network.client.IBibLibClient 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.factory.BookFactory +import com.pixelized.biblib.network.factory.GenreFactory import com.pixelized.biblib.network.factory.SeriesFactory import com.pixelized.biblib.repository.apiCache.IAPICacheRepository import com.pixelized.biblib.utils.exception.BookFetchException @@ -63,9 +65,11 @@ suspend fun loadAllBooks( repository: IBookRepository, bookFactory: BookFactory = BookFactory(), seriesFactory: SeriesFactory = SeriesFactory(), + genresFactory: GenreFactory = GenreFactory(), ): Boolean { val seriesResponse: SeriesListResponse = client.service.series() val bookResponse: BookListResponse = client.service.list() + val genreResponse: GenreListResponse = client.service.genre() return when { bookResponse.isError -> { @@ -76,11 +80,17 @@ suspend fun loadAllBooks( Log.e("loadAllBooks", seriesResponse.message ?: "") throw BookFetchException(seriesResponse.message) } + genreResponse.isError -> { + Log.e("loadAllBooks", genreResponse.message ?: "") + throw BookFetchException(genreResponse.message) + } else -> { // prepare a list of new book ids. val newIds = cache.new?.data?.map { it.book_id } ?: listOf() // 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. val books = bookResponse.data?.map { dto -> val isNew = newIds.contains(dto.book_id) @@ -88,6 +98,7 @@ suspend fun loadAllBooks( return@map bookFactory.fromListResponseToBook( response = dto, seriesHash = series, + genreHash = genres, isNew = isNew, newOrder = index ) diff --git a/app/src/main/java/com/pixelized/biblib/utils/exception/MandatoryFieldMissingException.kt b/app/src/main/java/com/pixelized/biblib/utils/exception/MandatoryFieldMissingException.kt index f32f61d..c4f6322 100644 --- a/app/src/main/java/com/pixelized/biblib/utils/exception/MandatoryFieldMissingException.kt +++ b/app/src/main/java/com/pixelized/biblib/utils/exception/MandatoryFieldMissingException.kt @@ -1,4 +1,7 @@ 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") \ No newline at end of file