Created
March 20, 2019 17:11
-
-
Save matdziu/496897555dfdb24f3eaa1af3dae0ad8a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com | |
| import com.TimeSpanType.FURTHER_THAN_NEXT_DAY | |
| import com.TimeSpanType.NEXT_DAY | |
| import com.TimeSpanType.SAME_DAY | |
| import org.junit.Assert.assertFalse | |
| import org.junit.Assert.assertTrue | |
| import org.junit.Test | |
| import java.text.SimpleDateFormat | |
| import java.util.Locale | |
| enum class TimeSpanType { | |
| SAME_DAY, NEXT_DAY, FURTHER_THAN_NEXT_DAY | |
| } | |
| data class Configuration(val daysNumber: Int, | |
| val uniqueTimestampsNumber: Int, | |
| val smallestBreakInMillis: Long) { | |
| init { | |
| if (daysNumber <= 0 || uniqueTimestampsNumber <= 0 || smallestBreakInMillis <= 0) | |
| throw IllegalArgumentException("Configuration parameters must be greater than 0") | |
| } | |
| } | |
| data class Timestamp(val value: String) { | |
| private val dateFormat: SimpleDateFormat by lazy { | |
| SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) | |
| } | |
| private val dayInMillis = 24 * 60 * 60 * 1000 | |
| fun isNewSession(previousTimestamp: Timestamp, smallestBreakInMillis: Long): Boolean { | |
| return (toMillis() - previousTimestamp.toMillis()) >= smallestBreakInMillis | |
| } | |
| fun getTimeSpan(previousTimestamp: Timestamp): TimeSpanType { | |
| val previousTimestampMillis = previousTimestamp.toMillis() | |
| val currentTimestampMillis = toMillis() | |
| val millisLeftToEOD = dayInMillis - previousTimestampMillis % dayInMillis | |
| val completedPreviousDayMillis = previousTimestampMillis + millisLeftToEOD | |
| return if ((currentTimestampMillis > completedPreviousDayMillis) | |
| && (currentTimestampMillis < completedPreviousDayMillis + dayInMillis)) | |
| NEXT_DAY | |
| else if (currentTimestampMillis < completedPreviousDayMillis) | |
| SAME_DAY | |
| else FURTHER_THAN_NEXT_DAY | |
| } | |
| private fun toMillis(): Long = dateFormat.parse(value).time | |
| } | |
| interface OpinionValidator { | |
| fun shouldAskOpinion(timestamps: List<Timestamp>): Boolean | |
| } | |
| class OpinionValidatorImpl(private val configuration: Configuration) : OpinionValidator { | |
| override fun shouldAskOpinion(timestamps: List<Timestamp>): Boolean { | |
| return isAtLeastUnique(configuration.uniqueTimestampsNumber, timestamps) | |
| && wasUsedEveryLast(configuration.daysNumber, timestamps.reversed()) | |
| } | |
| private fun isAtLeastUnique(n: Int, timestamps: List<Timestamp>): Boolean { | |
| val filteredTimestamps = arrayListOf<Timestamp>() | |
| // < 0; timestamps.count() ) | |
| for (timestampIndex in 0 until timestamps.count()) { | |
| val currentTimestamp = timestamps[timestampIndex] | |
| if (timestampIndex == 0) filteredTimestamps.add(currentTimestamp) | |
| else addIfNewSession(timestamps, filteredTimestamps, currentTimestamp, timestampIndex) | |
| } | |
| return filteredTimestamps.count() >= n | |
| } | |
| private fun addIfNewSession(timestamps: List<Timestamp>, | |
| filteredTimestamps: MutableList<Timestamp>, | |
| currentTimestamp: Timestamp, | |
| timestampIndex: Int) { | |
| if (timestampIndex >= 1) { | |
| val previousTimestamp = timestamps[timestampIndex - 1] | |
| if (currentTimestamp.isNewSession(previousTimestamp, configuration.smallestBreakInMillis)) { | |
| filteredTimestamps.add(currentTimestamp) | |
| } | |
| } | |
| } | |
| private fun wasUsedEveryLast(numberOfDays: Int, reversedTimestamps: List<Timestamp>): Boolean { | |
| var consecutiveDaysCounter = 1 | |
| // < 0; reversedTimestamps.count() - 1 ) | |
| for (timestampIndex in 0 until reversedTimestamps.count() - 1) { | |
| val currentTimestamp = reversedTimestamps[timestampIndex] | |
| consecutiveDaysCounter = increaseIfConsecutiveDay(consecutiveDaysCounter, reversedTimestamps, | |
| currentTimestamp, timestampIndex) | |
| if (consecutiveDaysCounter == numberOfDays) return true | |
| } | |
| return false | |
| } | |
| private fun increaseIfConsecutiveDay(currentCounter: Int, | |
| reversedTimestamps: List<Timestamp>, | |
| currentTimestamp: Timestamp, | |
| timestampIndex: Int): Int { | |
| return if (timestampIndex <= reversedTimestamps.count() - 1) { | |
| val previousTimestamp = reversedTimestamps[timestampIndex + 1] | |
| val timeSpanType = currentTimestamp.getTimeSpan(previousTimestamp) | |
| when (timeSpanType) { | |
| NEXT_DAY -> currentCounter + 1 | |
| SAME_DAY -> currentCounter | |
| FURTHER_THAN_NEXT_DAY -> currentCounter | |
| } | |
| } else 0 | |
| } | |
| } | |
| class Test { | |
| @Test | |
| fun basicTest() { | |
| val firstList = listOf( | |
| "2017-03-10 08:13:11", | |
| "2017-03-10 19:01:27", | |
| "2017-03-11 07:35:55", | |
| "2017-03-11 16:15:11", | |
| "2017-03-12 08:01:41", | |
| "2017-03-12 17:19:08") | |
| .map { Timestamp(it) } | |
| val secondList = listOf( | |
| "2017-03-10 18:58:11", | |
| "2017-03-10 19:01:27", | |
| "2017-03-11 07:35:55", | |
| "2017-03-11 16:15:11", | |
| "2017-03-12 08:01:41", | |
| "2017-03-12 17:19:08") | |
| .map { Timestamp(it) } | |
| val thirdList = listOf( | |
| "2017-03-08 17:11:13", | |
| "2017-03-11 17:22:47", | |
| "2017-03-11 19:23:51", | |
| "2017-03-11 22:03:12", | |
| "2017-03-12 08:32:04", | |
| "2017-03-12 13:19:08", | |
| "2017-03-12 17:19:08") | |
| .map { Timestamp(it) } | |
| val configuration = Configuration( | |
| daysNumber = 3, | |
| uniqueTimestampsNumber = 6, | |
| smallestBreakInMillis = 30 * 60 * 1000) | |
| val opinionValidator = OpinionValidatorImpl(configuration) | |
| assertTrue(opinionValidator.shouldAskOpinion(firstList)) | |
| assertFalse(opinionValidator.shouldAskOpinion(secondList)) | |
| assertFalse(opinionValidator.shouldAskOpinion(thirdList)) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment