Add wetter.com trigger
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 27 May 2016 05:31:59 +0000 (07:31 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 27 May 2016 05:31:59 +0000 (07:31 +0200)
build.gradle
src/main/kotlin/net/pterodactylus/rhynodge/webpages/weather/wettercom/WetterComTrigger.kt [new file with mode: 0644]
src/test/kotlin/net/pterodactylus/rhynodge/webpages/weather/wettercom/WetterComTriggerTest.kt [new file with mode: 0644]

index 12fa602..e8e89c2 100644 (file)
@@ -30,6 +30,9 @@ buildscript {
 
 repositories {
     mavenCentral()
+    maven {
+        url "http://dl.bintray.com/kotlinx/kotlinx"
+    }
 }
 
 dependencies {
@@ -44,6 +47,8 @@ dependencies {
     compile group: "com.fasterxml.jackson.core", name: "jackson-annotations", version: "2.1.2"
     compile group: "com.fasterxml.jackson.core", name: "jackson-databind", version: "2.1.2"
     compile group: "com.google.inject", name: "guice", version: "4.0"
+    compile group: "org.jetbrains.kotlinx", name: "kotlinx.html", version: "0.5.8"
+    compile group: "org.jetbrains.kotlinx", name: "kotlinx.html.jvm", version: "0.5.8"
 
     testCompile group: "junit", name: "junit", version:"4.12"
     testCompile group: "org.hamcrest", name: "hamcrest-library", version:"1.3"
diff --git a/src/main/kotlin/net/pterodactylus/rhynodge/webpages/weather/wettercom/WetterComTrigger.kt b/src/main/kotlin/net/pterodactylus/rhynodge/webpages/weather/wettercom/WetterComTrigger.kt
new file mode 100644 (file)
index 0000000..d25610a
--- /dev/null
@@ -0,0 +1,74 @@
+package net.pterodactylus.rhynodge.webpages.weather.wettercom
+
+import kotlinx.html.body
+import kotlinx.html.div
+import kotlinx.html.h1
+import kotlinx.html.head
+import kotlinx.html.html
+import kotlinx.html.img
+import kotlinx.html.stream.createHTML
+import kotlinx.html.style
+import net.pterodactylus.rhynodge.Reaction
+import net.pterodactylus.rhynodge.State
+import net.pterodactylus.rhynodge.Trigger
+import net.pterodactylus.rhynodge.output.DefaultOutput
+import net.pterodactylus.rhynodge.output.Output
+import java.text.DateFormat
+import java.time.temporal.ChronoUnit.HOURS
+import java.util.Locale
+
+/**
+ * TODO
+ *
+ * @author [David ‘Bombe’ Roden](mailto:bombe@pterodactylus.net)
+ */
+class WetterComTrigger : Trigger {
+
+    private val dateFormatter = DateFormat.getDateInstance(DateFormat.LONG, Locale.ENGLISH)
+    private var newState = false
+    private lateinit var currentState: WetterComState
+
+    override fun mergeStates(previousState: State, currentState: State): State? {
+        newState = previousState != currentState
+        this.currentState = currentState as WetterComState
+        return currentState
+    }
+
+    override fun triggers(): Boolean {
+        return newState
+    }
+
+    override fun output(reaction: Reaction): Output {
+        val output = DefaultOutput("The weather (according to wetter.com) on %s".format(dateFormatter.format(currentState.dateTime.toInstant().toEpochMilli())))
+        output.addText("text/html", htmlOutput())
+        return output
+    }
+
+    private fun htmlOutput(): String {
+        return createHTML().html {
+            head {
+                style("text/css") {
+                    +".hour-state { display: table-row; }"
+                    +".hour-state > div { display: table-cell; }"
+                }
+            }
+            body {
+                val startTime = currentState.dateTime.toInstant()
+                h1 { +"The Weather (according to wetter.com) on %s".format(dateFormatter.format(startTime.toEpochMilli())) }
+                currentState.forEach {
+                    div("hour-state") {
+                        div("time") { +"%tH:%<tM".format(startTime.plus(it.hourIndex.toLong(), HOURS).toEpochMilli()) }
+                        div("temperature") { +"%d °C".format(it.temperature.toInt()) }
+                        div("rain-probability") { +"%d%%".format((it.rainProbability * 100).toInt()) }
+                        div("rain-amount") { +"%d l/m²".format(it.rainAmount.toInt()) }
+                        div("wind-direction") { +it.windDirection.name }
+                        div("wind-speed") { +"%d km/h".format(it.windSpeed.toInt()) }
+                        div("description") { +it.description }
+                        div("image") { img(src = it.image) }
+                    }
+                }
+            }
+        }.toString()
+    }
+
+}
diff --git a/src/test/kotlin/net/pterodactylus/rhynodge/webpages/weather/wettercom/WetterComTriggerTest.kt b/src/test/kotlin/net/pterodactylus/rhynodge/webpages/weather/wettercom/WetterComTriggerTest.kt
new file mode 100644 (file)
index 0000000..2205dbb
--- /dev/null
@@ -0,0 +1,84 @@
+package net.pterodactylus.rhynodge.webpages.weather.wettercom
+
+import net.pterodactylus.rhynodge.Reaction
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.`is`
+import org.hamcrest.Matchers.containsString
+import org.junit.Test
+import org.mockito.Mockito
+import java.time.ZoneId
+import java.time.ZonedDateTime
+
+/**
+ * Unit test for [WetterComTrigger].
+
+ * @author [David ‘Bombe’ Roden](mailto:bombe@pterodactylus.net)
+ */
+class WetterComTriggerTest {
+
+    private val trigger = WetterComTrigger()
+    private val now = ZonedDateTime.now()
+    private val previousState = WetterComState(now)
+
+    @Test
+    fun equalStatesAreNotMerged() {
+        val currentState = WetterComState(now)
+        val newState = trigger.mergeStates(previousState, currentState) as WetterComState
+        assertThat(newState, `is`(previousState))
+        assertThat(newState, `is`(currentState))
+    }
+
+    @Test
+    fun currentStateIsReturnedIfDifferentFromPreviousState() {
+        val currentState = WetterComState(now.minusDays(1))
+        val newState = trigger.mergeStates(previousState, currentState) as WetterComState
+        assertThat(newState, `is`(currentState))
+    }
+
+    @Test
+    fun mergingEqualStatesDoesNotTrigger() {
+        val currentState = WetterComState(now)
+        trigger.mergeStates(previousState, currentState) as WetterComState
+        assertThat(trigger.triggers(), `is`(false))
+    }
+
+    @Test
+    fun mergingDifferentStatesDoesTrigger() {
+        val currentState = WetterComState(now.minusDays(1))
+        trigger.mergeStates(previousState, currentState) as WetterComState
+        assertThat(trigger.triggers(), `is`(true))
+    }
+
+    @Test
+    fun outputSummaryContainsCorrectDate() {
+        val currentState = WetterComState(ZonedDateTime.of(2000, 1, 1, 5, 0, 0, 0, ZoneId.of("Europe/Berlin")))
+        trigger.mergeStates(previousState, currentState) as WetterComState
+        val reaction = Mockito.mock(Reaction::class.java)
+        val output = trigger.output(reaction)
+        assertThat(output.summary(), `is`("The weather (according to wetter.com) on January 1, 2000"))
+    }
+
+    @Test
+    fun outputHtmlContainsHourStates() {
+        val currentState = WetterComState(now.minusDays(1))
+        currentState.addHour(HourState(0, 10.0, 0.11, 12.0, WindDirection.NORTH, 13.0, "Rain", "http://1"))
+        currentState.addHour(HourState(1, 14.0, 0.15, 16.0, WindDirection.SOUTH, 17.0, "Sun", "http://2"))
+        trigger.mergeStates(previousState, currentState) as WetterComState
+        val reaction = Mockito.mock(Reaction::class.java)
+        val output = trigger.output(reaction)
+        val htmlText = output.text("text/html", -1)
+        assertThat(htmlText, containsString("10 °C"))
+        assertThat(htmlText, containsString("11%"))
+        assertThat(htmlText, containsString("12 l/m"))
+        assertThat(htmlText, containsString("13 km/h"))
+        assertThat(htmlText, containsString("Rain"))
+        assertThat(htmlText, containsString("http://1"))
+        assertThat(htmlText, containsString("14 °C"))
+        assertThat(htmlText, containsString("15%"))
+        assertThat(htmlText, containsString("16 l/m"))
+        assertThat(htmlText, containsString("17 km/h"))
+        assertThat(htmlText, containsString("Sun"))
+        assertThat(htmlText, containsString("http://2"))
+    }
+
+}