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 import kotlinx.serialization.Serializable

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

@ -1,6 +1,6 @@
package com.jeluchu.core.extensions 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.jeluchu.core.utils.TimerKey
import com.mongodb.client.MongoCollection import com.mongodb.client.MongoCollection
import com.mongodb.client.model.Filters.eq import com.mongodb.client.model.Filters.eq

@ -1,6 +1,6 @@
package com.jeluchu.core.messages package com.jeluchu.core.messages
import com.jeluchu.core.utils.Day import com.jeluchu.core.enums.Day
sealed class ErrorMessages(val message: String) { sealed class ErrorMessages(val message: String) {
data object NotFound : ErrorMessages("Nyaaaaaaaan! This request has not been found by our alpaca-neko") 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 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 com.jeluchu.features.schedule.models.DayEntity
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -281,5 +283,26 @@ data class AnimeData(
image = images?.webp?.large.orEmpty(), image = images?.webp?.large.orEmpty(),
title = titles?.first()?.title.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 { object Endpoints {
const val SCHEDULES = "schedules" const val SCHEDULES = "schedules"
const val TOP_ANIME = "top/anime"
} }
object Routes { object Routes {
const val TOP = "/top"
const val ANIME = "/anime"
const val SCHEDULE = "/schedule" const val SCHEDULE = "/schedule"
const val DIRECTORY = "/directory" const val DIRECTORY = "/directory"
const val TOP_ANIME = "/top/anime"
const val TOP_MANGA = "/top/manga" const val TOP_MANGA = "/top/manga"
const val TOP_PEOPLE = "/top/people" const val TOP_PEOPLE = "/top/people"
const val ANIME_DETAILS = "/anime/{id}" const val ANIME_DETAILS = "/anime/{id}"
const val SCHEDULE_DAY = "/schedule/{day}" const val SCHEDULE_DAY = "/schedule/{day}"
const val TOP_CHARACTER = "/top/character" const val TOP_CHARACTER = "/top/character"
const val TOP_ANIME = "/{type}/{filter}/{page}"
} }
object TimerKey { object TimerKey {
const val KEY = "key" const val KEY = "key"
const val RANKING = "ranking"
const val SCHEDULE = "schedule" const val SCHEDULE = "schedule"
const val LAST_UPDATED = "lastUpdated" 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 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.DayEntity
import com.jeluchu.features.schedule.models.ScheduleData import com.jeluchu.features.schedule.models.ScheduleData
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
@ -24,5 +25,15 @@ fun parseScheduleDataToDocuments(data: ScheduleData): List<Document> {
processDay(data.saturday) processDay(data.saturday)
processDay(data.sunday) processDay(data.sunday)
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 return documents
} }

@ -4,6 +4,7 @@ import com.example.models.*
import com.jeluchu.core.extensions.* import com.jeluchu.core.extensions.*
import com.jeluchu.features.anime.models.anime.Images import com.jeluchu.features.anime.models.anime.Images
import com.jeluchu.features.anime.models.directory.AnimeDirectoryEntity import com.jeluchu.features.anime.models.directory.AnimeDirectoryEntity
import com.jeluchu.features.rankings.models.TopEntity
import com.jeluchu.features.schedule.models.DayEntity import com.jeluchu.features.schedule.models.DayEntity
import org.bson.Document import org.bson.Document
@ -219,4 +220,20 @@ fun documentToScheduleDayEntity(doc: Document) = DayEntity(
malId = doc.getIntSafe("malId"), malId = doc.getIntSafe("malId"),
image = doc.getStringSafe("image"), image = doc.getStringSafe("image"),
title = doc.getStringSafe("title") 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, mongoDatabase: MongoDatabase,
service: RankingsService = RankingsService(mongoDatabase) service: RankingsService = RankingsService(mongoDatabase)
) { ) {
getToJson(Routes.TOP_ANIME) { service.getAnimeByMalId(call) } route(Routes.TOP) {
getToJson(Routes.TOP_MANGA) { service.getDirectory(call) } route(Routes.ANIME) {
getToJson(Routes.TOP_ANIME) { service.getAnimeRanking(call) }
}
}
getToJson(Routes.TOP_MANGA) { }
} }

@ -1,16 +1,16 @@
package com.jeluchu.features.rankings.services package com.jeluchu.features.rankings.services
import com.jeluchu.core.connection.RestClient import com.jeluchu.core.connection.RestClient
import com.jeluchu.core.enums.*
import com.jeluchu.core.extensions.needsUpdate import com.jeluchu.core.extensions.needsUpdate
import com.jeluchu.core.extensions.update import com.jeluchu.core.extensions.update
import com.jeluchu.core.messages.ErrorMessages import com.jeluchu.core.messages.ErrorMessages
import com.jeluchu.core.models.ErrorResponse 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.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.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.jeluchu.features.schedule.models.ScheduleEntity
import com.mongodb.client.MongoDatabase import com.mongodb.client.MongoDatabase
import com.mongodb.client.model.Filters import com.mongodb.client.model.Filters
@ -25,52 +25,54 @@ class RankingsService(
database: MongoDatabase database: MongoDatabase
) { ) {
private val timers = database.getCollection("timers") private val timers = database.getCollection("timers")
private val schedules = database.getCollection("schedule") private val ranking = database.getCollection("ranking")
suspend fun getAnimeRanking(call: RoutingCall) { 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( val needsUpdate = timers.needsUpdate(
amount = 30, amount = 1,
unit = TimeUnit.DAY, key = timerKey,
key = TimerKey.SCHEDULE unit = TimeUnit.MINUTE
) )
if (needsUpdate) { if (needsUpdate) {
schedules.deleteMany(Document()) ranking.deleteMany(Document())
val response = ScheduleData( val params = mutableListOf<String>()
sunday = getSchedule(Day.SUNDAY).data?.map { it.toDayEntity(Day.SUNDAY) }.orEmpty(), params.add("type=$paramType")
friday = getSchedule(Day.FRIDAY).data?.map { it.toDayEntity(Day.FRIDAY) }.orEmpty(), params.add("page=$paramPage")
monday = getSchedule(Day.MONDAY).data?.map { it.toDayEntity(Day.MONDAY) }.orEmpty(), params.add("filter=$paramFilter")
tuesday = getSchedule(Day.TUESDAY).data?.map { it.toDayEntity(Day.TUESDAY) }.orEmpty(),
thursday = getSchedule(Day.THURSDAY).data?.map { it.toDayEntity(Day.THURSDAY) }.orEmpty(), val response = RestClient.request(
saturday = getSchedule(Day.SATURDAY).data?.map { it.toDayEntity(Day.SATURDAY) }.orEmpty(), BaseUrls.JIKAN + Endpoints.TOP_ANIME + "?${params.joinToString("&")}",
wednesday = getSchedule(Day.WEDNESDAY).data?.map { it.toDayEntity(Day.WEDNESDAY) }.orEmpty() Search.serializer()
) ).data?.mapIndexed { index, anime ->
anime.toTopEntity(
page = paramPage,
rank = index + 1,
type = paramType,
subType = paramFilter
)
}
val documentsToInsert = parseScheduleDataToDocuments(response) val documentsToInsert = parseScheduleDataToDocuments(response)
if (documentsToInsert.isNotEmpty()) schedules.insertMany(documentsToInsert) if (documentsToInsert.isNotEmpty()) ranking.insertMany(documentsToInsert)
timers.update(TimerKey.SCHEDULE) timers.update(timerKey)
call.respond(HttpStatusCode.OK, Json.encodeToString(response)) call.respond(HttpStatusCode.OK, Json.encodeToString(response))
} else { } else {
val elements = schedules.find().toList() val elements = ranking.find().toList()
val directory = elements.map { documentToScheduleDayEntity(it) } val directory = elements.map { documentToTopEntity(it) }
val json = Json.encodeToString(directory) val json = Json.encodeToString(directory)
call.respond(HttpStatusCode.OK, json) 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 package com.jeluchu.features.schedule.services
import com.jeluchu.core.connection.RestClient 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.needsUpdate
import com.jeluchu.core.extensions.update import com.jeluchu.core.extensions.update
import com.jeluchu.core.messages.ErrorMessages import com.jeluchu.core.messages.ErrorMessages

Loading…
Cancel
Save