From 8f345db8cac69a8ad2877d1b8fc73b6237d78fed Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Fri, 12 Jan 2024 16:29:43 +0100 Subject: [PATCH] Check-in: Add rule for number of days with entries since --- .../libpretixsync/check/AsyncCheckProvider.kt | 28 +++++++ .../check/AsyncCheckProviderTest.kt | 76 +++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/libpretixsync/src/main/java/eu/pretix/libpretixsync/check/AsyncCheckProvider.kt b/libpretixsync/src/main/java/eu/pretix/libpretixsync/check/AsyncCheckProvider.kt index 77dbd2b1..b97a155c 100644 --- a/libpretixsync/src/main/java/eu/pretix/libpretixsync/check/AsyncCheckProvider.kt +++ b/libpretixsync/src/main/java/eu/pretix/libpretixsync/check/AsyncCheckProvider.kt @@ -102,6 +102,20 @@ class AsyncCheckProvider(private val config: ConfigStore, private val dataStore: ) as DateTime ) } + jsonLogic.addOperation("entries_days_since") { l, d -> + ((d as Map<*, *>)["entries_days_since"] as ((DateTime) -> Int)).invoke( + l?.getOrNull( + 0 + ) as DateTime + ) + } + jsonLogic.addOperation("entries_days_before") { l, d -> + ((d as Map<*, *>)["entries_days_before"] as ((DateTime) -> Int)).invoke( + l?.getOrNull( + 0 + ) as DateTime + ) + } jsonLogic.addOperation("isAfter") { l, d -> if (l?.size == 2 || (l?.size == 3 && l.getOrNull(2) == null)) { (l.getOrNull(0) as DateTime).isAfter(l.getOrNull(1) as DateTime) @@ -719,11 +733,25 @@ class AsyncCheckProvider(private val config: ConfigStore, private val dataStore: DateTime(it.fullDatetime).withZone(tz).isAfter(cutoff.minus(Duration.millis(1))) && it.type == "entry" }.size }) + data.put("entries_days_since", { cutoff: DateTime -> + checkIns.filter { + DateTime(it.fullDatetime).withZone(tz).isAfter(cutoff.minus(Duration.millis(1))) && it.type == "entry" + }.map { + DateTime(it.fullDatetime).withZone(tz).toLocalDate() + }.toHashSet().size + }) data.put("entries_before", { cutoff: DateTime -> checkIns.filter { DateTime(it.fullDatetime).withZone(tz).isBefore(cutoff) && it.type == "entry" }.size }) + data.put("entries_days_before", { cutoff: DateTime -> + checkIns.filter { + DateTime(it.fullDatetime).withZone(tz).isBefore(cutoff) && it.type == "entry" + }.map { + DateTime(it.fullDatetime).withZone(tz).toLocalDate() + }.toHashSet().size + }) data.put("entries_days", checkIns.filter { it.type == "entry" }.map { DateTime(it.fullDatetime).withZone(tz).toLocalDate() }.toHashSet().size) diff --git a/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt b/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt index f9dac482..8f9e355e 100644 --- a/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt +++ b/libpretixsync/src/test/java/eu/pretix/libpretixsync/check/AsyncCheckProviderTest.kt @@ -572,6 +572,82 @@ class AsyncCheckProviderTest : BaseDatabaseTest() { assertEquals(TicketCheckProvider.CheckResult.Type.RULES, r.type) } + @Test + fun testRulesEntriesDaysSince() { + val p2 = AsyncCheckProvider(configStore!!, dataStore) + + // Ticket is valid once before X and on one day after X + setRuleOnList2("{" + + " \"or\": [\n" + + " {\"<=\": [{\"var\": \"entries_number\"}, 0]},\n" + + " {\"and\": [\n" + + " {\"isAfter\": [{\"var\": \"now\"}, {\"buildTime\": [\"custom\", \"2020-01-01T23:00:00.000+01:00\"]}, 0]},\n" + + " {\"or\": [\n" + + " {\">\": [{\"var\": \"entries_today\"}, 0]},\n" + + " {\"<=\": [{\"entries_days_since\": [{\"buildTime\": [\"custom\", \"2020-01-01T23:00:00.000+01:00\"]}]}, 0]},\n" + + " ]}\n" + + " ]},\n" + + " ],\n" + + " }") + + p2.setNow(ISODateTimeFormat.dateTime().parseDateTime("2020-01-01T21:00:00.000Z")) + + var r = p2.check(mapOf("demo" to 2L), "kfndgffgyw4tdgcacx6bb3bgemq69cxj") + assertEquals(TicketCheckProvider.CheckResult.Type.VALID, r.type) + + r = p2.check(mapOf("demo" to 2L), "kfndgffgyw4tdgcacx6bb3bgemq69cxj") + assertEquals(TicketCheckProvider.CheckResult.Type.RULES, r.type) + + p2.setNow(ISODateTimeFormat.dateTime().parseDateTime("2020-01-02T22:10:00.000Z")) + + r = p2.check(mapOf("demo" to 2L), "kfndgffgyw4tdgcacx6bb3bgemq69cxj") + assertEquals(TicketCheckProvider.CheckResult.Type.VALID, r.type) + + r = p2.check(mapOf("demo" to 2L), "kfndgffgyw4tdgcacx6bb3bgemq69cxj") + assertEquals(TicketCheckProvider.CheckResult.Type.VALID, r.type) + + p2.setNow(ISODateTimeFormat.dateTime().parseDateTime("2020-01-03T22:10:00.000Z")) + + r = p2.check(mapOf("demo" to 2L), "kfndgffgyw4tdgcacx6bb3bgemq69cxj") + assertEquals(TicketCheckProvider.CheckResult.Type.RULES, r.type) + } + + @Test + fun testRulesEntriesDaysBefore() { + val p2 = AsyncCheckProvider(configStore!!, dataStore) + + // Ticket is valid after 23:00 only if people already showed up on two days before + setRuleOnList2("{" + + " \"or\": [\n" + + " {\"isBefore\": [{\"var\": \"now\"}, {\"buildTime\": [\"custom\", \"2020-01-01T23:00:00.000+01:00\"]}, 0]},\n" + + " {\"and\": [\n" + + " {\"isAfter\": [{\"var\": \"now\"}, {\"buildTime\": [\"custom\", \"2020-01-01T23:00:00.000+01:00\"]}, 0]},\n" + + " {\">=\": [{\"entries_days_before\": [{\"buildTime\": [\"custom\", \"2020-01-01T23:00:00.000+01:00\"]}]}, 2]},\n" + + " ]},\n" + + " ],\n" + + " }") + + p2.setNow(ISODateTimeFormat.dateTime().parseDateTime("2019-12-30T21:00:00.000Z")) + + var r = p2.check(mapOf("demo" to 2L), "kfndgffgyw4tdgcacx6bb3bgemq69cxj") + assertEquals(TicketCheckProvider.CheckResult.Type.VALID, r.type) + + p2.setNow(ISODateTimeFormat.dateTime().parseDateTime("2020-01-02T22:10:00.000Z")) + + r = p2.check(mapOf("demo" to 2L), "kfndgffgyw4tdgcacx6bb3bgemq69cxj") + assertEquals(TicketCheckProvider.CheckResult.Type.RULES, r.type) + + p2.setNow(ISODateTimeFormat.dateTime().parseDateTime("2019-12-31T21:00:00.000Z")) + + r = p2.check(mapOf("demo" to 2L), "kfndgffgyw4tdgcacx6bb3bgemq69cxj") + assertEquals(TicketCheckProvider.CheckResult.Type.VALID, r.type) + + p2.setNow(ISODateTimeFormat.dateTime().parseDateTime("2020-01-02T22:10:00.000Z")) + + r = p2.check(mapOf("demo" to 2L), "kfndgffgyw4tdgcacx6bb3bgemq69cxj") + assertEquals(TicketCheckProvider.CheckResult.Type.VALID, r.type) + } + @Test fun testRulesMinutesSinceLastEntry() { val p2 = AsyncCheckProvider(configStore!!, dataStore)