Skip to content

Commit

Permalink
Sett opp caching av opplysninger og mulige regler
Browse files Browse the repository at this point in the history
  • Loading branch information
androa committed Dec 23, 2024
1 parent 4b4f128 commit 7de1e9e
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ class KontrollpunktTest {

val opplysninger = Opplysninger()
val regelkjøring = Regelkjøring(1.mai(2024), opplysninger)
opplysninger.leggTil(getOpplysning(321) as Opplysning<*>).also { regelkjøring.evaluer() }
opplysninger.leggTil(getOpplysning(321))
regelkjøring.evaluer()

val ding = Avklaringer(kontrollpunkter)
ding.måAvklares(opplysninger).also { avklaringer ->
Expand Down Expand Up @@ -101,7 +102,7 @@ class KontrollpunktTest {

val opplysninger = Opplysninger()
val regelkjøring = Regelkjøring(1.mai(2024), opplysninger)
opplysninger.leggTil(Faktum<Boolean>(inntekterInneholderSykepenger, true) as Opplysning<*>).also { regelkjøring.evaluer() }
opplysninger.leggTil(Faktum(inntekterInneholderSykepenger, true)).also { regelkjøring.evaluer() }

val ding = Avklaringer(kontrollpunkter)
ding.måAvklares(opplysninger).also { avklaringer ->
Expand Down Expand Up @@ -152,7 +153,7 @@ class KontrollpunktTest {
avklaringer.all { it.kode == BeregningsregelForFVA } shouldBe true
}

opplysninger.leggTil(Faktum<Boolean>(regel1, true) as Opplysning<*>).also { regelkjøring.evaluer() }
opplysninger.leggTil(Faktum(regel1, true)).also { regelkjøring.evaluer() }

// Denne avklaringen skal ikke kunne kvitteres ut, den krever endring
shouldThrow<IllegalArgumentException> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package no.nav.dagpenger.opplysning

class CachedList<T>(
val compute: () -> List<T>,
) : List<T> {
private var cachedValue: List<T>? = null
private val value: List<T>
get() = cachedValue ?: refresh()

fun refresh(): List<T> {
cachedValue = compute()
return cachedValue!!
}

override val size get() = value.size

override fun contains(element: T) = value.contains(element)

override fun containsAll(elements: Collection<T>) = value.containsAll(elements)

override fun get(index: Int) = value[index]

override fun indexOf(element: T) = value.indexOf(element)

override fun isEmpty() = value.isEmpty()

override fun iterator() = value.iterator()

override fun lastIndexOf(element: T) = value.lastIndexOf(element)

override fun listIterator() = value.listIterator()

override fun listIterator(index: Int) = value.listIterator(index)

override fun subList(
fromIndex: Int,
toIndex: Int,
) = value.subList(fromIndex, toIndex)
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ sealed class Opplysning<T : Comparable<T>>(
}

abstract fun lagErstatning(opplysning: Opplysning<T>): Opplysning<T>

companion object {
fun Collection<Opplysning<*>>.bareAktive() = filterNot { it.erErstattet || it.erFjernet }

fun Collection<Opplysning<*>>.gyldigeFor(dato: LocalDate) = filter { it.gyldighetsperiode.inneholder(dato) }
}
}

class Hypotese<T : Comparable<T>>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
package no.nav.dagpenger.opplysning

import no.nav.dagpenger.opplysning.Opplysning.Companion.bareAktive
import no.nav.dagpenger.opplysning.Opplysning.Companion.gyldigeFor
import no.nav.dagpenger.uuid.UUIDv7
import java.lang.Exception
import java.time.LocalDate
import java.util.UUID
import kotlin.collections.filter
import kotlin.collections.filterNot
import kotlin.collections.find
import kotlin.collections.toList

class Opplysninger private constructor(
override val id: UUID,
opplysninger: List<Opplysning<*>> = emptyList(),
initielleOpplysninger: List<Opplysning<*>> = emptyList(),
basertPå: List<Opplysninger> = emptyList(),
) : LesbarOpplysninger {
private val opplysninger: MutableList<Opplysning<*>> = opplysninger.toMutableList()
private val basertPåOpplysninger: List<Opplysning<*>> = basertPå.flatMap { it.basertPåOpplysninger + it.opplysninger }.toList()

// TODO: Vi må se på om denne kan få bedre ytelse
private val alleOpplysninger: List<Opplysning<*>>
get() = (basertPåOpplysninger + opplysninger).filterNot { it.erErstattet }.filterNot { it.erFjernet }

constructor() : this(UUIDv7.ny(), emptyList(), emptyList())
constructor(id: UUID, opplysninger: List<Opplysning<*>>) : this(id, opplysninger, emptyList())
constructor(opplysninger: List<Opplysning<*>>, basertPå: List<Opplysninger> = emptyList()) : this(UUIDv7.ny(), opplysninger, basertPå)
constructor(vararg basertPå: Opplysninger) : this(emptyList(), basertPå.toList())

private val basertPåOpplysninger: List<Opplysning<*>> =
basertPå.flatMap { it.basertPåOpplysninger + it.opplysninger }.bareAktive()

private val opplysninger: MutableList<Opplysning<*>> = initielleOpplysninger.toMutableList()
private val alleOpplysninger = CachedList { basertPåOpplysninger + opplysninger.bareAktive() }

val aktiveOpplysninger get() = opplysninger.toList()

override fun forDato(gjelderFor: LocalDate): LesbarOpplysninger {
// TODO: Erstatt med noe collectorgreier får å unngå at opplysninger som er erstattet blir med
val opplysningerForDato =
opplysninger
.filter { it.gyldighetsperiode.inneholder(gjelderFor) }
.filterNot { it.erErstattet }
.filterNot { it.erFjernet }
val opplysningerForDato = opplysninger.bareAktive().gyldigeFor(gjelderFor)
return Opplysninger(UUIDv7.ny(), opplysningerForDato)
}

Expand All @@ -40,6 +40,7 @@ class Opplysninger private constructor(

if (eksisterende == null) {
opplysninger.add(opplysning)
alleOpplysninger.refresh()
return
}

Expand Down Expand Up @@ -70,6 +71,7 @@ class Opplysninger private constructor(
throw IllegalArgumentException("Kan ikke legge til opplysning som overlapper med eksisterende opplysning")
}
}
alleOpplysninger.refresh()
return
}
}
Expand All @@ -78,10 +80,12 @@ class Opplysninger private constructor(
// Erstatt hele opplysningen
eksisterende.fjern()
opplysninger.add(opplysning)
alleOpplysninger.refresh()
return
}

opplysninger.add(opplysning)
alleOpplysninger.refresh()
}

internal fun <T : Comparable<T>> leggTilUtledet(opplysning: Opplysning<T>) = leggTil(opplysning)
Expand Down Expand Up @@ -127,6 +131,16 @@ class Opplysninger private constructor(
opplysning.gyldighetsperiode.inneholder(this.gyldighetsperiode.tom)

operator fun plus(tidligereOpplysninger: List<Opplysninger>) = Opplysninger(id, opplysninger, tidligereOpplysninger)

fun fjern(opplysningstyper: Set<Opplysningstype<*>>) {
opplysninger
.filterNot {
opplysningstyper.contains(it.opplysningstype)
}.forEach {
it.fjern()
}
alleOpplysninger.refresh()
}
}

class OpplysningIkkeFunnetException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,7 @@ class Regelkjøring(
}

val brukteOpplysninger = muligeOpplysninger()

// TODO: La oppførselen ligge i opplysninger
opplysningerPåPrøvingsdato
.finnAlle()
.filterNot {
brukteOpplysninger.contains(it.opplysningstype)
}.forEach {
it.fjern()
}
opplysninger.fjern(brukteOpplysninger)

return Regelkjøringsrapport(
kjørteRegler = kjørteRegler,
Expand Down Expand Up @@ -148,11 +140,12 @@ class Regelkjøring(

private fun aktiverRegler() {
val produksjonsplan = mutableSetOf<Regel<*>>()
val produsenter = gjeldendeRegler.associateBy { it.produserer }
ønsketResultat.forEach { opplysningstype ->
val produsent =
gjeldendeRegler.singleOrNull { it.produserer(opplysningstype) }
produsenter[opplysningstype]
?: throw IllegalArgumentException("Fant ikke regel som produserer $opplysningstype")
produsent.lagPlan(opplysningerPåPrøvingsdato, produksjonsplan, gjeldendeRegler)
produsent.lagPlan(opplysningerPåPrøvingsdato, produksjonsplan, produsenter)
}
val (ekstern, intern) = produksjonsplan.partition { it is Ekstern<*> }
plan = intern.toMutableSet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ abstract class Regel<T : Comparable<T>> internal constructor(
internal open fun lagPlan(
opplysninger: LesbarOpplysninger,
plan: MutableSet<Regel<*>>,
gjeldendeRegler: List<Regel<*>>,
produsenter: Map<Opplysningstype<*>, Regel<*>>,
) {
if (plan.contains(this)) return

if (opplysninger.har(produserer)) {
val produkt = opplysninger.finnOpplysning(produserer)
if (produkt.utledetAv == null) {
return
}

produkt.utledetAv.opplysninger.forEach { avhengighet ->
val produsent = gjeldendeRegler.singleOrNull { it.produserer(avhengighet.opplysningstype) }
produsent?.lagPlan(opplysninger, plan, gjeldendeRegler)
val produsent = produsenter[avhengighet.opplysningstype]
produsent?.lagPlan(opplysninger, plan, produsenter)
}

val avhengighetErErstattet = produkt.utledetAv.opplysninger.any { it.erErstattet || it.erFjernet }
Expand All @@ -42,10 +44,8 @@ abstract class Regel<T : Comparable<T>> internal constructor(
return
} else {
avhengerAv.forEach { avhengighet ->
val produsent =
gjeldendeRegler.singleOrNull { it.produserer(avhengighet) }
?: throw IllegalStateException("Fant ikke produsent for $avhengighet")
produsent.lagPlan(opplysninger, plan, gjeldendeRegler)
val produsent = produsenter[avhengighet] ?: throw IllegalStateException("Fant ikke produsent for $avhengighet")
produsent.lagPlan(opplysninger, plan, produsenter)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class DagensDato internal constructor(
override fun lagPlan(
opplysninger: LesbarOpplysninger,
plan: MutableSet<Regel<*>>,
gjeldendeRegler: List<Regel<*>>,
produsenter: Map<Opplysningstype<out Comparable<*>>, Regel<*>>,
) {
if (opplysninger.har(produserer)) return
val dag = opplysninger.finnOpplysning(produserer).verdi
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package no.nav.dagpenger.opplysning

import io.kotest.matchers.collections.shouldContainExactly
import org.junit.jupiter.api.Test

class CachedListTest {
@Test
fun `cacher lister som er mutable på utsiden`() {
var basertPå = mutableListOf(1)
var externalList = mutableListOf(2, 3)
val cachedList = CachedList { basertPå + externalList.filter { it != 5 } }

cachedList.shouldContainExactly(1, 2, 3)

externalList.add(4)
cachedList.shouldContainExactly(1, 2, 3)

cachedList.refresh()
cachedList.shouldContainExactly(1, 2, 3, 4)
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
package no.nav.dagpenger.opplysning

import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.shouldBe
import io.kotest.matchers.collections.shouldBeEmpty
import io.kotest.matchers.collections.shouldContainExactly
import no.nav.dagpenger.opplysning.TestOpplysningstyper.desimaltall
import no.nav.dagpenger.opplysning.TestOpplysningstyper.foreldrevilkår
import no.nav.dagpenger.opplysning.TestOpplysningstyper.undervilkår1
import no.nav.dagpenger.opplysning.TestOpplysningstyper.undervilkår2
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals

@Disabled(
"""Vi har endret på hvordan vi behandler opplysninger. Vi erstatter alltid når det er i samme behandling,
|og endrer kun når de går på tvers av behandlinger""",
)
class OpplysningerTest {
@Test
fun `vilkår er avhengig av andre vilkår`() {
Expand All @@ -33,77 +27,13 @@ class OpplysningerTest {
}

@Test
fun `Ny opplysning overlapper på halen av eksisterende opplysning`() {
// Og skal endre tilOgMed for eksisterende opplysning
// Og skal legge til ny opplysning
fun `caching av opplysninger oppdateres`() {
val opplysninger = Opplysninger()
opplysninger.finnAlle().shouldBeEmpty()

val original = Faktum(desimaltall, 0.5, Gyldighetsperiode(fom = 1.mai))
val nyOpplysning = Faktum(desimaltall, 1.5, Gyldighetsperiode(fom = 11.mai))
opplysninger.leggTil(original)
opplysninger.leggTil(nyOpplysning)
val opplysning = Faktum(desimaltall, 0.5)
opplysninger.leggTil(opplysning)

assertEquals(0.5, opplysninger.forDato(10.mai).finnOpplysning(desimaltall).verdi)
assertEquals(1.5, opplysninger.forDato(15.mai).finnOpplysning(desimaltall).verdi)

opplysninger.forDato(5.mai).finnOpplysning(desimaltall).erstatter shouldBe original
opplysninger.forDato(15.mai).finnOpplysning(desimaltall).erstatter shouldBe null
}

@Test
fun `Ny opplysning overlapper samme periode`() {
// Da skal vi erstatte gammel opplysning med ny
val opplysninger = Opplysninger()

val original = Faktum(desimaltall, 0.5, Gyldighetsperiode(fom = 1.mai))
val nyOpplysning = Faktum(desimaltall, 1.5, Gyldighetsperiode(fom = 1.mai))
opplysninger.leggTil(original)
opplysninger.leggTil(nyOpplysning)

assertEquals(1.5, opplysninger.forDato(1.mai).finnOpplysning(desimaltall).verdi)
assertEquals(1.5, opplysninger.forDato(15.mai).finnOpplysning(desimaltall).verdi)

opplysninger.forDato(5.mai).finnOpplysning(desimaltall).erstatter shouldBe original
}

@Test
fun `Ny opplysning overlapper på starten av eksisterende opplysning`() {
// Da skal vi erstatte gammel opplysning med ny
val opplysninger = Opplysninger()

val original = Faktum(desimaltall, 0.5, Gyldighetsperiode(fom = 5.mai, tom = 20.mai))
val nyOpplysning = Faktum(desimaltall, 1.5, Gyldighetsperiode(fom = 1.mai, tom = 16.mai))
opplysninger.leggTil(original)
opplysninger.leggTil(nyOpplysning)

assertEquals(1.5, opplysninger.forDato(1.mai).finnOpplysning(desimaltall).verdi)
assertEquals(1.5, opplysninger.forDato(15.mai).finnOpplysning(desimaltall).verdi)

shouldThrow<Exception> {
assertEquals(1.5, opplysninger.forDato(18.mai).finnOpplysning(desimaltall).verdi)
}

opplysninger.forDato(5.mai).finnOpplysning(desimaltall).erstatter shouldBe original
}

@Test
fun `Ny opplysning overlapper på midten av eksisterende opplysning`() {
// Da skal vi erstatte gammel opplysning med forkortet opplysning og legge til ny opplysning
val opplysninger = Opplysninger()

val original = Faktum(desimaltall, 0.5, Gyldighetsperiode(fom = 1.mai, tom = 30.mai))
val nyOpplysning = Faktum(desimaltall, 1.5, Gyldighetsperiode(fom = 10.mai, tom = 20.mai))
opplysninger.leggTil(original)
opplysninger.leggTil(nyOpplysning)

assertEquals(0.5, opplysninger.forDato(1.mai).finnOpplysning(desimaltall).verdi)
assertEquals(1.5, opplysninger.forDato(15.mai).finnOpplysning(desimaltall).verdi)

shouldThrow<Exception> {
assertEquals(1.5, opplysninger.forDato(21.mai).finnOpplysning(desimaltall).verdi)
}

opplysninger.forDato(5.mai).finnOpplysning(desimaltall).erstatter shouldBe original
opplysninger.forDato(15.mai).finnOpplysning(desimaltall).erstatter shouldBe null
opplysninger.finnAlle().shouldContainExactly(opplysning)
}
}

0 comments on commit 7de1e9e

Please sign in to comment.