From: David ‘Bombe’ Roden Date: Wed, 31 Jul 2019 14:37:56 +0000 (+0200) Subject: 💄 Format times on metrics page better X-Git-Tag: v81^2~141 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=6579401cd867c5ade1ebf098d64d85dc2016defe 💄 Format times on metrics page better --- diff --git a/src/main/kotlin/net/pterodactylus/sone/template/DurationFormatFilter.kt b/src/main/kotlin/net/pterodactylus/sone/template/DurationFormatFilter.kt new file mode 100644 index 0000000..559c9da --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/template/DurationFormatFilter.kt @@ -0,0 +1,67 @@ +package net.pterodactylus.sone.template + +import net.pterodactylus.util.template.* +import java.time.* + +class DurationFormatFilter : Filter { + + override fun format(templateContext: TemplateContext?, data: Any?, parameters: Map?): Any? { + if (data is Number) { + val scale = parameters?.get("scale") + val duration = when (scale) { + "ms" -> Duration.ofSeconds(data.toLong() / 1_000, (data.toDouble() * 1_000_000 % 1_000_000_000).toLong()) + "μs" -> Duration.ofSeconds(data.toLong() / 1_000_000, (data.toDouble() * 1_000 % 1_000_000_000).toLong()) + "ns" -> Duration.ofSeconds(data.toLong() / 1_000_000_000, data.toLong() % 1_000_000_000) + else -> Duration.ofSeconds(data.toLong(), (data.toDouble() * 1_000_000_000 % 1_000_000_000).toLong()) + } + return FixedDuration.values() + .map { it to it.number(duration) } + .firstOrNull { it.second >= 1 } + ?.let { "${"%.1f".format(it.second)}${it.first.symbol}" } + ?: "0s" + } + return data + } + +} + +@Suppress("unused") +private enum class FixedDuration { + + WEEKS { + override fun number(duration: Duration) = DAYS.number(duration) / 7.0 + override val symbol = "w" + }, + DAYS { + override fun number(duration: Duration) = HOURS.number(duration) / 24 + override val symbol = "d" + }, + HOURS { + override fun number(duration: Duration) = MINUTES.number(duration) / 60 + override val symbol = "h" + }, + MINUTES { + override fun number(duration: Duration) = SECONDS.number(duration) / 60 + override val symbol = "m" + }, + SECONDS { + override fun number(duration: Duration) = duration.seconds + duration.nano / 1_000_000_000.0 + override val symbol = "s" + }, + MILLIS { + override fun number(duration: Duration) = duration.nano / 1_000_000.0 + override val symbol = "ms" + }, + MICROS { + override fun number(duration: Duration) = duration.nano / 1_1000.0 + override val symbol = "μs" + }, + NANOS { + override fun number(duration: Duration) = duration.nano.toDouble() + override val symbol = "ns" + }; + + abstract fun number(duration: Duration): Double + abstract val symbol: String + +} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt b/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt index dd5e9f6..283b1ea 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/WebInterfaceModule.kt @@ -65,6 +65,7 @@ class WebInterfaceModule : AbstractModule() { addFilter("reparse", ReparseFilter()) addFilter("unknown", unknownDateFilter) addFilter("format", FormatFilter()) + addFilter("duration", DurationFormatFilter()) addFilter("sort", CollectionSortFilter()) addFilter("image-link", imageLinkFilter) addFilter("replyGroup", ReplyGroupFilter()) diff --git a/src/main/resources/templates/metrics.html b/src/main/resources/templates/metrics.html index 2880e78..beaded8 100644 --- a/src/main/resources/templates/metrics.html +++ b/src/main/resources/templates/metrics.html @@ -24,28 +24,28 @@ <%= Page.Metrics.SoneInsertDuration.Title|l10n|html> <% soneInsertDurationCount|html> - <% soneInsertDurationMin|html>μs - <% soneInsertDurationMax|html>μs - <% soneInsertDurationMean|format format=='%.0f'|html>μs - <% soneInsertDurationMedian|format format=='%.0f'|html>μs - <% soneInsertDurationPercentile75|format format=='%.0f'|html>μs - <% soneInsertDurationPercentile95|format format=='%.0f'|html>μs - <% soneInsertDurationPercentile98|format format=='%.0f'|html>μs - <% soneInsertDurationPercentile99|format format=='%.0f'|html>μs - <% soneInsertDurationPercentile999|format format=='%.0f'|html>μs + <% soneInsertDurationMin|duration scale=="μs"|html> + <% soneInsertDurationMax|duration scale=="μs"|html> + <% soneInsertDurationMean|duration scale=="μs"|html> + <% soneInsertDurationMedian|duration scale=="μs"|html> + <% soneInsertDurationPercentile75|duration scale=="μs"|html> + <% soneInsertDurationPercentile95|duration scale=="μs"|html> + <% soneInsertDurationPercentile98|duration scale=="μs"|html> + <% soneInsertDurationPercentile99|duration scale=="μs"|html> + <% soneInsertDurationPercentile999|duration scale=="μs"|html> <%= Page.Metrics.SoneParsingDuration.Title|l10n|html> <% soneParsingDurationCount|html> - <% soneParsingDurationMin|html>μs - <% soneParsingDurationMax|html>μs - <% soneParsingDurationMean|format format=='%.0f'|html>μs - <% soneParsingDurationMedian|format format=='%.0f'|html>μs - <% soneParsingDurationPercentile75|format format=='%.0f'|html>μs - <% soneParsingDurationPercentile95|format format=='%.0f'|html>μs - <% soneParsingDurationPercentile98|format format=='%.0f'|html>μs - <% soneParsingDurationPercentile99|format format=='%.0f'|html>μs - <% soneParsingDurationPercentile999|format format=='%.0f'|html>μs + <% soneParsingDurationMin|duration scale=="μs"|html> + <% soneParsingDurationMax|duration scale=="μs"|html> + <% soneParsingDurationMean|duration scale=="μs"|html> + <% soneParsingDurationMedian|duration scale=="μs"|html> + <% soneParsingDurationPercentile75|duration scale=="μs"|html> + <% soneParsingDurationPercentile95|duration scale=="μs"|html> + <% soneParsingDurationPercentile98|duration scale=="μs"|html> + <% soneParsingDurationPercentile99|duration scale=="μs"|html> + <% soneParsingDurationPercentile999|duration scale=="μs"|html> diff --git a/src/test/kotlin/net/pterodactylus/sone/template/DurationFormatFilterTest.kt b/src/test/kotlin/net/pterodactylus/sone/template/DurationFormatFilterTest.kt new file mode 100644 index 0000000..9e378a8 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/template/DurationFormatFilterTest.kt @@ -0,0 +1,88 @@ +/** + * Sone - DurationFormatFilterTest.kt - Copyright © 2019 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.template + +import org.hamcrest.MatcherAssert.* +import org.hamcrest.Matchers.* +import kotlin.test.* + +class DurationFormatFilterTest { + + private val filter = DurationFormatFilter() + + @Test + fun `random object is returned as it is`() { + val randomObject = Any() + assertThat(filter.format(null, randomObject, emptyMap()), sameInstance(randomObject)) + } + + @Test + fun `integer 0 is rendered as “0s”`() { + verifyDuration(0, "0s") + } + + @Test + fun `long 0 is rendered as “0s”`() { + verifyDuration(0L, "0s") + } + + @Test + fun `12 is rendered as “12_0s”`() { + verifyDuration(12, "12.0s") + } + + @Test + fun `123 is rendered as “2_1m”`() { + verifyDuration(123, "2.1m") + } + + @Test + fun `12345 is rendered as “3_4h”`() { + verifyDuration(12345, "3.4h") + } + + @Test + fun `123456 is rendered as “1_4d”`() { + verifyDuration(123456, "1.4d") + } + + @Test + fun `1234567 is rendered as “2_0w”`() { + verifyDuration(1234567, "2.0w") + } + + @Test + fun `123456789 with scale ms is rendered as “1_4d”`() { + verifyDuration(123456789, "1.4d", "ms") + } + + @Test + fun `123456789 with scale μs is rendered as “2_1m”`() { + verifyDuration(123456789, "2.1m", "μs") + } + + @Test + fun `123456789 with scale ns is rendered as “123_5ms”`() { + verifyDuration(123456789, "123.5ms", "ns") + } + + private fun verifyDuration(value: Any, expectedRendering: String, scale: String? = null) { + assertThat(filter.format(null, value, scale?.let { mapOf("scale" to scale) } ?: emptyMap()), equalTo(expectedRendering)) + } + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt index 9137da3..ec09b20 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt @@ -20,6 +20,7 @@ import net.pterodactylus.util.web.* import org.hamcrest.MatcherAssert.* import org.hamcrest.Matchers.* import org.junit.* +import kotlin.test.Test class WebInterfaceModuleTest { @@ -200,6 +201,11 @@ class WebInterfaceModuleTest { } @Test + fun `template context contains duration format filter`() { + verifyFilter("duration") + } + + @Test fun `template context contains collection sort filter`() { verifyFilter("sort") }