Improve request for Ranking endpoints

v5
Jéluchu 9 months ago
parent dfc1b5a2b8
commit a98078029f

@ -0,0 +1,13 @@
package com.jeluchu.core.enums
import kotlinx.serialization.Serializable
@Serializable
enum class AnimeFilterTypes {
AIRING,
UPCOMING,
BYPOPULARITY,
FAVORITE,
}
fun parseFilterType(type: String) = AnimeFilterTypes.entries.firstOrNull { it.name.equals(type, ignoreCase = true) }

@ -0,0 +1,18 @@
package com.jeluchu.core.enums
import kotlinx.serialization.Serializable
@Serializable
enum class AnimeTypes {
TV,
MOVIE,
OVA,
SPECIAL,
ONA,
MUSIC,
CM,
PV,
TV_SPECIAL,
}
fun parseType(type: String) = AnimeTypes.entries.firstOrNull { it.name.equals(type, ignoreCase = true) }

@ -1,4 +1,4 @@
package com.jeluchu.core.utils
package com.jeluchu.core.enums
import kotlinx.serialization.Serializable

@ -1,4 +1,4 @@
package com.jeluchu.core.utils
package com.jeluchu.core.enums
enum class TimeUnit {
DAY,

@ -1,6 +1,6 @@
package com.jeluchu.core.extensions
import com.jeluchu.core.utils.TimeUnit
import com.jeluchu.core.enums.TimeUnit
import com.jeluchu.core.utils.TimerKey
import com.mongodb.client.MongoCollection
import com.mongodb.client.model.Filters.eq

@ -1,6 +1,6 @@
package com.jeluchu.core.messages
import com.jeluchu.core.utils.Day
import com.jeluchu.core.enums.Day
sealed class ErrorMessages(val message: String) {
data object NotFound : ErrorMessages("Nyaaaaaaaan! This request has not been found by our alpaca-neko")

@ -1,6 +1,8 @@
package com.jeluchu.core.models.jikan.anime
import com.jeluchu.core.utils.Day
import com.jeluchu.core.enums.Day
import com.jeluchu.core.utils.toVideoPromo
import com.jeluchu.features.rankings.models.TopEntity
import com.jeluchu.features.schedule.models.DayEntity
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@ -281,5 +283,26 @@ data class AnimeData(
image = images?.webp?.large.orEmpty(),
title = titles?.first()?.title.orEmpty()
)
fun AnimeData.toTopEntity(
page: Int,
rank: Int,
type: String,
subType: String,
) = TopEntity(
malId = malId,
rank = rank,
score = score,
image = images?.webp?.large.orEmpty(),
title = titles?.first()?.title.orEmpty(),
url = url,
promo = trailer?.toVideoPromo(),
season = season,
year = year,
airing = airing,
type = type,
subtype = subType,
page = page
)
}
}

@ -6,22 +6,26 @@ object BaseUrls {
object Endpoints {
const val SCHEDULES = "schedules"
const val TOP_ANIME = "top/anime"
}
object Routes {
const val TOP = "/top"
const val ANIME = "/anime"
const val SCHEDULE = "/schedule"
const val DIRECTORY = "/directory"
const val TOP_ANIME = "/top/anime"
const val TOP_MANGA = "/top/manga"
const val TOP_PEOPLE = "/top/people"
const val ANIME_DETAILS = "/anime/{id}"
const val SCHEDULE_DAY = "/schedule/{day}"
const val TOP_CHARACTER = "/top/character"
const val TOP_ANIME = "/{type}/{filter}/{page}"
}
object TimerKey {
const val KEY = "key"
const val RANKING = "ranking"
const val SCHEDULE = "schedule"
const val LAST_UPDATED = "lastUpdated"
}

@ -0,0 +1,21 @@
package com.jeluchu.core.utils
import com.example.models.VideoPromo
import com.jeluchu.core.models.jikan.anime.ImageFormat
import com.jeluchu.core.models.jikan.anime.Trailer
import com.jeluchu.features.anime.models.anime.Images
fun Trailer.toVideoPromo() = VideoPromo(
url = url.orEmpty(),
youtubeId = youtubeId.orEmpty(),
embedUrl = embedUrl.orEmpty(),
images = images?.toImages() ?: Images()
)
fun ImageFormat.toImages() = Images(
generic = generic.orEmpty(),
small = small.orEmpty(),
medium = medium.orEmpty(),
large = large.orEmpty(),
maximum = maximum.orEmpty()
)

@ -1,5 +1,6 @@
package com.jeluchu.core.utils
import com.jeluchu.features.rankings.models.TopEntity
import com.jeluchu.features.schedule.models.DayEntity
import com.jeluchu.features.schedule.models.ScheduleData
import kotlinx.serialization.encodeToString
@ -26,3 +27,13 @@ fun parseScheduleDataToDocuments(data: ScheduleData): List<Document> {
return documents
}
fun parseScheduleDataToDocuments(data: List<TopEntity>?): List<Document> {
val documents = mutableListOf<Document>()
data?.forEach { animeData ->
val animeJsonString = Json.encodeToString(animeData)
val document = Document.parse(animeJsonString)
documents.add(document)
}
return documents
}

@ -4,6 +4,7 @@ import com.example.models.*
import com.jeluchu.core.extensions.*
import com.jeluchu.features.anime.models.anime.Images
import com.jeluchu.features.anime.models.directory.AnimeDirectoryEntity
import com.jeluchu.features.rankings.models.TopEntity
import com.jeluchu.features.schedule.models.DayEntity
import org.bson.Document
@ -220,3 +221,19 @@ fun documentToScheduleDayEntity(doc: Document) = DayEntity(
image = doc.getStringSafe("image"),
title = doc.getStringSafe("title")
)
fun documentToTopEntity(doc: Document) = TopEntity(
malId = doc.getIntSafe("malId"),
rank = doc.getIntSafe("rank"),
score = doc.getFloatSafe("score"),
title = doc.getStringSafe("title"),
image = doc.getStringSafe("image"),
url = doc.getStringSafe("url"),
promo = doc.getDocumentSafe("promo")?.let { documentToVideoPromo(it) } ?: VideoPromo(),
season = doc.getStringSafe("season"),
year = doc.getIntSafe("year"),
airing = doc.getBooleanSafe("airing"),
type = doc.getStringSafe("type"),
subtype = doc.getStringSafe("subtype"),
page = doc.getIntSafe("page"),
)

@ -0,0 +1,21 @@
package com.jeluchu.features.rankings.models
import com.example.models.VideoPromo
import kotlinx.serialization.Serializable
@Serializable
data class TopEntity(
val malId: Int? = 0,
val rank: Int? = 0,
val score: Float? = 0f,
val title: String? = "",
val image: String? = "",
val url: String? = "",
val promo: VideoPromo? = VideoPromo(),
val season: String? = "",
val year: Int? = 0,
val airing: Boolean? = false,
val type: String? = "",
val subtype: String? = "",
val page: Int? = 0
)

@ -10,6 +10,11 @@ fun Route.rankingsEndpoints(
mongoDatabase: MongoDatabase,
service: RankingsService = RankingsService(mongoDatabase)
) {
getToJson(Routes.TOP_ANIME) { service.getAnimeByMalId(call) }
getToJson(Routes.TOP_MANGA) { service.getDirectory(call) }
route(Routes.TOP) {
route(Routes.ANIME) {
getToJson(Routes.TOP_ANIME) { service.getAnimeRanking(call) }
}
}
getToJson(Routes.TOP_MANGA) { }
}

@ -1,16 +1,16 @@
package com.jeluchu.features.rankings.services
import com.jeluchu.core.connection.RestClient
import com.jeluchu.core.enums.*
import com.jeluchu.core.extensions.needsUpdate
import com.jeluchu.core.extensions.update
import com.jeluchu.core.messages.ErrorMessages
import com.jeluchu.core.models.ErrorResponse
import com.jeluchu.core.models.jikan.anime.AnimeData.Companion.toDayEntity
import com.jeluchu.core.models.jikan.anime.AnimeData.Companion.toTopEntity
import com.jeluchu.core.models.jikan.search.Search
import com.jeluchu.core.utils.*
import com.jeluchu.features.anime.mappers.documentToAnimeDirectoryEntity
import com.jeluchu.features.anime.mappers.documentToMoreInfoEntity
import com.jeluchu.features.anime.mappers.documentToScheduleDayEntity
import com.jeluchu.features.schedule.models.ScheduleData
import com.jeluchu.features.anime.mappers.documentToTopEntity
import com.jeluchu.features.schedule.models.ScheduleEntity
import com.mongodb.client.MongoDatabase
import com.mongodb.client.model.Filters
@ -25,52 +25,54 @@ class RankingsService(
database: MongoDatabase
) {
private val timers = database.getCollection("timers")
private val schedules = database.getCollection("schedule")
private val ranking = database.getCollection("ranking")
suspend fun getAnimeRanking(call: RoutingCall) {
val paramType = call.parameters["type"] ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message)
val paramFilter = call.parameters["filter"] ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message)
val paramPage = call.parameters["page"]?.toInt() ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message)
if (parseType(paramType) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidDay.message))
if (parseFilterType(paramFilter) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidDay.message))
val timerKey = "${TimerKey.RANKING}_${paramType}_${paramFilter}_${paramPage}"
val needsUpdate = timers.needsUpdate(
amount = 30,
unit = TimeUnit.DAY,
key = TimerKey.SCHEDULE
amount = 1,
key = timerKey,
unit = TimeUnit.MINUTE
)
if (needsUpdate) {
schedules.deleteMany(Document())
ranking.deleteMany(Document())
val params = mutableListOf<String>()
params.add("type=$paramType")
params.add("page=$paramPage")
params.add("filter=$paramFilter")
val response = ScheduleData(
sunday = getSchedule(Day.SUNDAY).data?.map { it.toDayEntity(Day.SUNDAY) }.orEmpty(),
friday = getSchedule(Day.FRIDAY).data?.map { it.toDayEntity(Day.FRIDAY) }.orEmpty(),
monday = getSchedule(Day.MONDAY).data?.map { it.toDayEntity(Day.MONDAY) }.orEmpty(),
tuesday = getSchedule(Day.TUESDAY).data?.map { it.toDayEntity(Day.TUESDAY) }.orEmpty(),
thursday = getSchedule(Day.THURSDAY).data?.map { it.toDayEntity(Day.THURSDAY) }.orEmpty(),
saturday = getSchedule(Day.SATURDAY).data?.map { it.toDayEntity(Day.SATURDAY) }.orEmpty(),
wednesday = getSchedule(Day.WEDNESDAY).data?.map { it.toDayEntity(Day.WEDNESDAY) }.orEmpty()
val response = RestClient.request(
BaseUrls.JIKAN + Endpoints.TOP_ANIME + "?${params.joinToString("&")}",
Search.serializer()
).data?.mapIndexed { index, anime ->
anime.toTopEntity(
page = paramPage,
rank = index + 1,
type = paramType,
subType = paramFilter
)
}
val documentsToInsert = parseScheduleDataToDocuments(response)
if (documentsToInsert.isNotEmpty()) schedules.insertMany(documentsToInsert)
timers.update(TimerKey.SCHEDULE)
if (documentsToInsert.isNotEmpty()) ranking.insertMany(documentsToInsert)
timers.update(timerKey)
call.respond(HttpStatusCode.OK, Json.encodeToString(response))
} else {
val elements = schedules.find().toList()
val directory = elements.map { documentToScheduleDayEntity(it) }
val elements = ranking.find().toList()
val directory = elements.map { documentToTopEntity(it) }
val json = Json.encodeToString(directory)
call.respond(HttpStatusCode.OK, json)
}
}
suspend fun getScheduleByDay(call: RoutingCall) {
val param = call.parameters["day"] ?: throw IllegalArgumentException(ErrorMessages.InvalidMalId.message)
if (parseDay(param) == null) call.respond(HttpStatusCode.BadRequest, ErrorResponse(ErrorMessages.InvalidDay.message))
val elements = schedules.find(Filters.eq("day", param.lowercase())).toList()
val directory = elements.map { documentToScheduleDayEntity(it) }
val json = Json.encodeToString(directory)
call.respond(HttpStatusCode.OK, json)
}
private suspend fun getSchedule(day: Day) =
RestClient.request(BaseUrls.JIKAN + Endpoints.SCHEDULES + "/" + day, ScheduleEntity.serializer())
}

@ -1,6 +1,9 @@
package com.jeluchu.features.schedule.services
import com.jeluchu.core.connection.RestClient
import com.jeluchu.core.enums.Day
import com.jeluchu.core.enums.TimeUnit
import com.jeluchu.core.enums.parseDay
import com.jeluchu.core.extensions.needsUpdate
import com.jeluchu.core.extensions.update
import com.jeluchu.core.messages.ErrorMessages

Loading…
Cancel
Save