Skip to content

Commit

Permalink
log datetime last usage for rules and platform simulcasts
Browse files Browse the repository at this point in the history
  • Loading branch information
Ziedelth committed Dec 27, 2024
1 parent 0df5f79 commit 462212b
Show file tree
Hide file tree
Showing 18 changed files with 116 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import fr.shikkanime.dtos.RuleDto
import fr.shikkanime.entities.Rule
import fr.shikkanime.entities.enums.Platform
import fr.shikkanime.services.RuleService
import java.time.ZonedDateTime

class RuleDtoToRuleConverter : AbstractConverter<RuleDto, Rule>() {
@Inject
Expand All @@ -23,7 +24,8 @@ class RuleDtoToRuleConverter : AbstractConverter<RuleDto, Rule>() {
seriesId = from.seriesId,
seasonId = from.seasonId,
action = from.action,
actionValue = from.actionValue
actionValue = from.actionValue,
lastUsageDateTime = from.lastUsageDateTime?.let { ZonedDateTime.parse(it) }
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ class RuleToRuleDtoConverter : AbstractConverter<Rule, RuleDto>() {
seriesId = from.seriesId!!,
seasonId = from.seasonId!!,
action = from.action!!,
actionValue = from.actionValue!!
actionValue = from.actionValue!!,
lastUsageDateTime = from.lastUsageDateTime?.withUTCString()
)
}
}
3 changes: 2 additions & 1 deletion src/main/kotlin/fr/shikkanime/dtos/RuleDto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ data class RuleDto(
val seriesId: String,
val seasonId: String,
val action: Rule.Action,
val actionValue: String
val actionValue: String,
val lastUsageDateTime: String?,
)
4 changes: 3 additions & 1 deletion src/main/kotlin/fr/shikkanime/entities/Rule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ class Rule(
@Enumerated(EnumType.STRING)
var action: Action? = null,
@Column(name = "action_value", nullable = false)
var actionValue: String? = null
var actionValue: String? = null,
@Column(name = "last_usage_date_time", nullable = true)
var lastUsageDateTime: ZonedDateTime? = null,
) : ShikkEntity(uuid) {
enum class Action {
REPLACE_ANIME_NAME,
Expand Down
7 changes: 7 additions & 0 deletions src/main/kotlin/fr/shikkanime/platforms/AbstractPlatform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,11 @@ abstract class AbstractPlatform<C : PlatformConfiguration<*>, K : Any, V> {

private fun getConfigurationFile() =
File(Constant.configFolder, "${getPlatform().platformName.lowercase().replace(" ", "-")}.json")

fun updateAnimeSimulcast(name: String) {
configuration!!.simulcasts.find { it.name.lowercase() == name.lowercase() }?.let {
it.lastUsageDateTime = ZonedDateTime.now().withUTCString()
saveConfiguration()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,15 @@ class AnimationDigitalNetworkPlatform :
if (configuration!!.blacklistedSimulcasts.contains(animeName.lowercase())) throw AnimeException("\"$animeName\" is blacklisted")

val genres = video.show.genres
val isConfigurationSimulcast = configuration!!.containsAnimeSimulcast(animeName)

val contains = configuration!!.simulcasts.map { it.name.lowercase() }.contains(animeName.lowercase())
if ((genres.isEmpty() || !genres.any { it.startsWith("Animation ", true) }) && !contains && checkAnimation)
if ((genres.isEmpty() || !genres.any { it.startsWith("Animation ", true) }) && !isConfigurationSimulcast && checkAnimation)
throw Exception("Anime is not an animation")

if (needSimulcast) {
val isSimulcasted = video.show.simulcast ||
video.show.firstReleaseYear in (0..1).map { (zonedDateTime.year - it).toString() } ||
contains ||
isConfigurationSimulcast ||
configCacheService.getValueAsString(ConfigPropertyKey.ANIMATION_DITIGAL_NETWORK_SIMULCAST_DETECTION_REGEX)
?.let { Regex(it).containsMatchIn((video.show.summary.normalize() ?: "").lowercase()) } == true

Expand All @@ -99,6 +99,9 @@ class AnimationDigitalNetworkPlatform :

val (number, episodeType) = getNumberAndEpisodeType(video.shortNumber, video.type)

if (needSimulcast)
updateAnimeSimulcast(animeName)

return video.languages.map {
Episode(
countryCode = countryCode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ class CrunchyrollPlatform : AbstractPlatform<CrunchyrollConfiguration, CountryCo
throw EpisodeNoSubtitlesOrVoiceException("Episode is not available in ${countryCode.name} with subtitles or voice")

val crunchyrollAnimeContent = runBlocking { CrunchyrollCachedWrapper.getSeries(countryCode.locale, browseObject.episodeMetadata.seriesId) }
val isConfigurationSimulcast = configuration!!.simulcasts.any { it.name.lowercase() == animeName.lowercase() }
val isConfigurationSimulcast = configuration!!.containsAnimeSimulcast(animeName)
val season = runBlocking { CrunchyrollCachedWrapper.getSeason(countryCode.locale, browseObject.episodeMetadata.seasonId) }

val (number, episodeType) = getNumberAndEpisodeType(browseObject.episodeMetadata, season)
Expand All @@ -202,6 +202,9 @@ class CrunchyrollPlatform : AbstractPlatform<CrunchyrollConfiguration, CountryCo
original = currentVersion?.original != false
}

if (needSimulcast)
updateAnimeSimulcast(animeName)

return Episode(
countryCode = countryCode,
animeId = browseObject.episodeMetadata.seriesId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ data class ConfigurationField(
open class PlatformSimulcast(
var uuid: UUID? = null,
var name: String = "",
var lastUsageDateTime: String? = null,
) {
open fun of(parameters: Parameters) {
parameters["name"]?.let { name = it }
Expand All @@ -37,13 +38,15 @@ open class PlatformSimulcast(

if (uuid != other.uuid) return false
if (name != other.name) return false
if (lastUsageDateTime != other.lastUsageDateTime) return false

return true
}

override fun hashCode(): Int {
var result = uuid?.hashCode() ?: 0
result = 31 * result + name.hashCode()
result = 31 * result + (lastUsageDateTime?.hashCode() ?: 0)
return result
}
}
Expand All @@ -59,6 +62,8 @@ abstract class PlatformConfiguration<S : PlatformSimulcast>(
@Suppress("UNCHECKED_CAST")
fun addPlatformSimulcast(simulcast: PlatformSimulcast) = simulcasts.add(simulcast as S)

fun containsAnimeSimulcast(name: String) = simulcasts.any { it.name.lowercase() == name.lowercase() }

open fun of(parameters: Parameters) {
parameters["availableCountries"]?.let {
availableCountries = if (it.isNotBlank()) CountryCode.from(it.split(",")) else emptySet()
Expand Down
11 changes: 11 additions & 0 deletions src/main/kotlin/fr/shikkanime/repositories/RuleRepository.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
package fr.shikkanime.repositories

import fr.shikkanime.entities.Rule
import fr.shikkanime.entities.Rule_

class RuleRepository : AbstractRepository<Rule>() {
override fun getEntityClass() = Rule::class.java

override fun findAll(): List<Rule> {
return database.entityManager.use {
val cb = it.criteriaBuilder
val query = cb.createQuery(getEntityClass())
val root = query.from(getEntityClass())
query.orderBy(cb.asc(root[Rule_.creationDateTime]))
createReadOnlyQuery(it, query).resultList
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class EpisodeVariantService : AbstractService<EpisodeVariant, EpisodeVariantRepo
@Inject
private lateinit var ruleCacheService: RuleCacheService

@Inject
private lateinit var ruleService: RuleService

override fun getRepository() = episodeVariantRepository

fun findAllIdentifierByDateRangeWithoutNextEpisode(
Expand Down Expand Up @@ -84,13 +87,17 @@ class EpisodeVariantService : AbstractService<EpisodeVariant, EpisodeVariantRepo
}

fun save(episode: AbstractPlatform.Episode, updateMappingDateTime: Boolean = true, episodeMapping: EpisodeMapping? = null): EpisodeVariant {
val now = ZonedDateTime.now()
val rules = ruleCacheService.findAllByPlatformSeriesIdAndSeasonId(episode.platform, episode.animeId, episode.seasonId)

rules.forEach { rule ->
when (rule.action!!) {
Rule.Action.REPLACE_ANIME_NAME -> episode.anime = rule.actionValue!!
Rule.Action.REPLACE_SEASON_NUMBER -> episode.season = rule.actionValue!!.toInt()
}

rule.lastUsageDateTime = now
ruleService.update(rule)
}

val animeName = StringUtils.removeAnimeNamePart(episode.anime)
Expand Down
14 changes: 12 additions & 2 deletions src/main/resources/assets/js/dashboard_chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ const loginCountChart = new Chart(loginCountChartElement, {
type: 'line',
data: { labels: [], datasets: [
{ label: 'Login distinct count', data: [], fill: true, borderColor: 'rgb(255,255,0)', backgroundColor: 'rgba(255,255,0,0.5)', tension: 0.1 },
{ label: 'Login count', data: [], fill: true, borderColor: 'rgb(255,122,0)', backgroundColor: 'rgba(255,122,0,0.5)', tension: 0.1 }
{ label: 'Login distinct count trending', data: [], fill: false, borderColor: 'rgb(255,0,0)', tension: 0.1 },
{ label: 'Login count', data: [], fill: true, borderColor: 'rgb(255,122,0)', backgroundColor: 'rgba(255,122,0,0.5)', tension: 0.1 },
{ label: 'Login count trending', data: [], fill: false, borderColor: 'rgb(0,255,0)', tension: 0.1 }
] },
options: {
maintainAspectRatio: false,
Expand Down Expand Up @@ -54,6 +56,14 @@ const setChartData = async () => {
const loginCounts = await getLoginCounts();
loginCountChart.data.labels = loginCounts.map(loginCount => loginCount.date);
loginCountChart.data.datasets[0].data = loginCounts.map(loginCount => loginCount.distinctCount);
loginCountChart.data.datasets[1].data = loginCounts.map(loginCount => loginCount.count);
loginCountChart.data.datasets[1].data = loginCounts.map((_, i) => {
const sum = loginCounts.slice(Math.max(0, i - 6), i + 1).reduce((acc, loginCount) => acc + loginCount.distinctCount, 0);
return sum / Math.min(i + 1, 7);
});
loginCountChart.data.datasets[2].data = loginCounts.map(loginCount => loginCount.count);
loginCountChart.data.datasets[3].data = loginCounts.map((_, i) => {
const sum = loginCounts.slice(Math.max(0, i - 6), i + 1).reduce((acc, loginCount) => acc + loginCount.count, 0);
return sum / Math.min(i + 1, 7);
});
loginCountChart.update();
};
23 changes: 23 additions & 0 deletions src/main/resources/db/changelog/2024/12/03-changelog.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.30.xsd"
objectQuotingStrategy="QUOTE_ONLY_RESERVED_WORDS">
<property global="false" name="id" value="1735332942177"/>
<property global="false" name="author" value="Ziedelth"/>

<changeSet id="${id}-1" author="${author}">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="rule" columnName="last_usage_date_time"/>
</not>
</preConditions>

<addColumn tableName="rule">
<column name="last_usage_date_time"
type="timestamp"/>
</addColumn>
</changeSet>
</databaseChangeLog>
1 change: 1 addition & 0 deletions src/main/resources/db/changelog/db.changelog-master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,5 @@
<!-- December 2024 -->
<include file="/db/changelog/2024/12/01-changelog.xml"/>
<include file="/db/changelog/2024/12/02-changelog.xml"/>
<include file="/db/changelog/2024/12/03-changelog.xml"/>
</databaseChangeLog>
11 changes: 10 additions & 1 deletion src/main/resources/templates/admin/dashboard.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,21 @@
<div class="ms-md-auto me-md-0">
<a id="simulcasts-invalidate" href="/admin/simulcasts-invalidate"
class="btn btn-danger">Invalidate</a>
<button id="simulcasts-show" class="btn btn-success">Show all</button>
</div>

<script>
document.getElementById('simulcasts-show').addEventListener('click', function () {
document.querySelectorAll('.list-group-item.d-none').forEach(function (element) {
element.classList.remove('d-none');
});
});
</script>
</div>

<ul class="list-group list-group-numbered">
<#list simulcasts as simulcast>
<li class="list-group-item d-flex justify-content-between align-items-start">
<li class="list-group-item d-flex justify-content-between align-items-start <#if simulcast_index<5><#else>d-none</#if>">
<div class="ms-2 me-auto">
<div class="fw-bold">${simulcast.season} ${simulcast.year?c}</div>
</div>
Expand Down
17 changes: 15 additions & 2 deletions src/main/resources/templates/admin/platforms/list.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,20 @@
<a href="/admin/platforms/${abstractPlatform.platform.name()}/simulcasts/${simulcast.uuid}"
class="card text-decoration-none mb-3">
<div class="card-body">
${simulcast.name}
<div class="row">
<div class="col-md-10">
${simulcast.name}
</div>
<div class="col-md-2 text-md-end text-muted">
<i class="bi bi-clock-history me-2"></i>

<#if simulcast.lastUsageDateTime??>
${simulcast.lastUsageDateTime?datetime.iso?string["dd/MM/yyyy HH:mm:ss"]}
<#else>
Never
</#if>
</div>
</div>
</div>
</a>
</#list>
Expand All @@ -66,7 +79,7 @@
<hr class="my-3">

<div class="d-flex">
<button type="submit" class="btn btn-primary ms-auto mr-0">Update</button>
<button type="submit" class="btn btn-success ms-auto mr-0">Update</button>
</div>
</div>
</form>
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/templates/admin/rules.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@

<button type="submit" class="btn btn-success ms-auto me-0" :disabled="loading" data-bs-toggle="modal"
data-bs-target="#createModal" @click="selected = {platform: {}}">
<i class="bi bi-plus-circle me-2"></i>
Create
</button>
</div>
Expand All @@ -113,6 +114,7 @@
<th scope="col">Season ID</th>
<th scope="col">Action name</th>
<th scope="col">Action value</th>
<th scope="col">Last usage</th>
<th scope="col">Actions</th>
</tr>
</thead>
Expand All @@ -124,6 +126,7 @@
<td x-text="rule.seasonId"></td>
<td x-text="rule.action"></td>
<td x-text="rule.actionValue"></td>
<td x-text="rule.lastUsageDateTime ? new Date(rule.lastUsageDateTime).toLocaleString() : 'Never'"></td>
<td>
<button type="button" class="btn btn-danger" data-bs-toggle="modal"
data-bs-target="#deleteModal" @click="selected = rule">
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/templates/admin/trace-actions.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
<p class="mb-0">
<a x-show="traceAction.action !== 'DELETE'" x-bind:href="traceAction.entityType === 'Anime' ? '/admin/animes/' + traceAction.entityUuid :
traceAction.entityType === 'EpisodeMapping' ? '/admin/episodes/' + traceAction.entityUuid :
traceAction.entityType === 'Member' ? '/admin/members/' + traceAction.entityUuid :
`#`"
class="text-muted text-decoration-none"
x-text="traceAction.entityType"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class CrunchyrollPlatformTest : AbstractTest() {
assertEquals("Metallic Rouge", episodes[1].anime)
assertEquals("ja-JP", episodes[1].audioLocale)
assertNotNull(episodes[1].description)
assertNotNull(platform.configuration?.simulcasts?.first()?.lastUsageDateTime)
}

@Test
Expand Down

0 comments on commit 462212b

Please sign in to comment.