Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Legg til hjelpefunksjoner for tidslinje fra familie-felles #5073

Merged
merged 12 commits into from
Feb 14, 2025
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@
<groupId>no.nav.familie.felles</groupId>
<artifactId>metrikker</artifactId>
</dependency>
<dependency>
<groupId>no.nav.familie.felles</groupId>
<artifactId>tidslinje</artifactId>
</dependency>
<dependency>
<groupId>no.nav.familie.kontrakter</groupId>
<artifactId>felles</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.komposisjon

import no.nav.familie.tidslinje.Tidslinje
import no.nav.familie.tidslinje.tomTidslinje
import no.nav.familie.tidslinje.utvidelser.kombinerMed

/**
* Extension-metode for å kombinere to nøkkel-verdi-map'er der verdiene er tidslinjer
* Nøkkelen må være av samme type, K, tidslinjene må være i samme tidsenhet (T)
* Innholdet i tidslinjene i map'en på venstre side må alle være av typen V
* Innholdet i tidslinjene i map'en på høyre side må alle være av typen H
* Kombinator-funksjonen kalles med verdiene av fra venstre og høyre tidslinje for samme nøkkel og tidspunkt.
* <null> blir sendt som verdier hvis venstre, høyre eller begge tidslinjer mangler verdi for et tidspunkt
* Resultatet er en ny map der nøklene er av type K, og tidslinjene har innhold av typen (nullable) R.
* Bare nøkler som finnes i begge map'ene vil finnes i den resulterende map'en
*/
fun <K, V, H, R> Map<K, Tidslinje<V>>.join(
høyreTidslinjer: Map<K, Tidslinje<H>>,
kombinator: (V?, H?) -> R?,
): Map<K, Tidslinje<R>> {
val venstreTidslinjer = this
val alleNøkler = venstreTidslinjer.keys.intersect(høyreTidslinjer.keys)

return alleNøkler.associateWith { nøkkel ->
val venstreTidslinje = venstreTidslinjer.getOrDefault(nøkkel, tomTidslinje())
val høyreTidslinje = høyreTidslinjer.getOrDefault(nøkkel, tomTidslinje())

venstreTidslinje.kombinerMed(høyreTidslinje, kombinator)
}
}

/**
* Extension-metode for å kombinere to nøkkel-verdi-map'er der verdiene er tidslinjer
* Nøkkelen må være av samme type, K, tidslinjene må være i samme tidsenhet (T)
* Innholdet i tidslinjene i map'en på venstre side må alle være av typen V
* Innholdet i tidslinjene i map'en på høyre side må alle være av typen H
* Kombinator-funksjonen kalles med verdiene av fra venstre og høyre tidslinje for samme nøkkel og tidspunkt.
* Kombinator-funksjonen blir IKKE kalt Hvis venstre, høyre eller begge tidslinjer mangler verdi for et tidspunkt
* Resultatet er en ny map der nøklene er av type K, og tidslinjene har innhold av typen (nullable) R.
* Bare nøkler som finnes i begge map'ene vil finnes i den resulterende map'en
*/
fun <K, V, H, R> Map<K, Tidslinje<V>>.joinIkkeNull(
høyreTidslinjer: Map<K, Tidslinje<H>>,
kombinator: (V, H) -> R?,
): Map<K, Tidslinje<R>> {
val venstreTidslinjer = this
val alleNøkler = venstreTidslinjer.keys.intersect(høyreTidslinjer.keys)

return alleNøkler.associateWith { nøkkel ->
val venstreTidslinje = venstreTidslinjer.getOrDefault(nøkkel, tomTidslinje())
val høyreTidslinje = høyreTidslinjer.getOrDefault(nøkkel, tomTidslinje())

venstreTidslinje.kombinerUtenNullMed(høyreTidslinje, kombinator)
}
}

/**
* Extension-metode for å kombinere en nøkkel-verdi-map'er der verdiene er tidslinjer, med en enkelt tidslinje
* Innholdet i tidslinjene i map'en på venstre side må alle være av typen V
* Innholdet i tidslinjen på høyre side er av typen H
* Kombinator-funksjonen kalles for hvert tidspunkt med med verdien for det tidspunktet fra høyre tidslinje og
* vedien fra den enkelte av venstre tidslinjer etter tur.
* Kombinator-funksjonen blir IKKE kalt Hvis venstre, høyre eller begge tidslinjer mangler verdi for et tidspunkt
* Resultatet er en ny map der nøklene er av type K, og tidslinjene har innhold av typen (nullable) R.
*/
fun <K, V, H, R> Map<K, Tidslinje<V>>.kombinerKunVerdiMed(
høyreTidslinje: Tidslinje<H>,
kombinator: (V, H) -> R?,
): Map<K, Tidslinje<R>> {
val venstreTidslinjer = this

return venstreTidslinjer.mapValues { (_, venstreTidslinje) ->
venstreTidslinje.kombinerUtenNullMed(høyreTidslinje, kombinator)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.komposisjon

import no.nav.familie.tidslinje.Null
import no.nav.familie.tidslinje.Tidslinje
import no.nav.familie.tidslinje.Udefinert
import no.nav.familie.tidslinje.Verdi
import no.nav.familie.tidslinje.tilPeriodeVerdi
import no.nav.familie.tidslinje.utvidelser.biFunksjon
import no.nav.familie.tidslinje.utvidelser.kombinerMed
import no.nav.familie.tidslinje.utvidelser.map
import no.nav.familie.tidslinje.utvidelser.slåSammen

/**
* Extension-metode for å kombinere to tidslinjer der begge har verdi
* Kombinasjonen baserer seg på å iterere gjennom alle tidspunktene
* fra minste fraOgMed til største tilOgMed fra begge tidslinjene
* Tidsenhet (T) må være av samme type
* Hver av tidslinjene kan ha ulik innholdstype, hhv V og H
* Hvis innholdet V eller H mangler innhold, så vil ikke resulterende tidslinje få innhold for det tidspunktet
* Kombintor-funksjonen tar ellers V og H og returnerer (nullable) R
* Hvis kombinator-funksjonen returner <null>, antas det at tidslinjen ikke skal ha verdi for tidspunktet
* Resultatet er en tidslinje med tidsenhet T og innhold R
*/
fun <V, H, R> Tidslinje<V>.kombinerUtenNullMed(
høyreTidslinje: Tidslinje<H>,
kombineringsfunksjon: (V, H) -> R?,
): Tidslinje<R> =
this.biFunksjon(høyreTidslinje) { periodeverdiVenstre, periodeverdiHøyre ->
when {
periodeverdiVenstre is Verdi && periodeverdiHøyre is Verdi ->
kombineringsfunksjon(periodeverdiVenstre.verdi, periodeverdiHøyre.verdi).tilPeriodeVerdi()

else -> Null()
}
}

/**
* Extension-metode for å kombinere liste av tidslinjer
* Kombinasjonen baserer seg på å iterere gjennom alle tidspunktene
* fra minste <fraOgMed()> til største <tilOgMed()> fra alle tidslinjene
* Innhold (I) og tidsenhet (T) må være av samme type
* Kombintor-funksjonen tar inn Iterable<I> og returner (nullable) R
* Null-verdier fjernes før de sendes til kombinator-funksjonen, som betyr at en tom iterator kan bli sendt
* Hvis reesultatet fra kombinatoren er null, tolkes det som at det ikke skal være innhold
* Resultatet er en tidslinje med tidsenhet T og innhold R
*/
fun <V, R> Collection<Tidslinje<V>>.kombinerUtenNull(
listeKombinator: (Iterable<V>) -> R?,
): Tidslinje<R> = kombinerNullableKombinator { it.filterNotNull().let(listeKombinator) }

/**
* Extension-metode for å kombinere liste av tidslinjer
* Kombinasjonen baserer seg på å iterere gjennom alle tidspunktene
* fra minste <fraOgMed()> til største <tilOgMed()> fra alle tidslinjene
* Innhold (I) og tidsenhet (T) må være av samme type
* Kombintor-funksjonen tar inn Iterable<I> og returner (nullable) R
* Null-verdier fjernes, og listen av verdier sendes til kombinator-funksjonen bare hvis den inneholder verdier
* Hvis reesultatet fra kombinatoren er null, tolkes det som at det ikke skal være innhold
* Resultatet er en tidslinje med tidsenhet T og innhold R
*/
fun <V, R> Collection<Tidslinje<V>>.kombinerUtenNullOgIkkeTom(
listeKombinator: (Iterable<V>) -> R?,
): Tidslinje<R> = kombinerNullableKombinator { it.filterNotNull().takeIf { it.isNotEmpty() }?.let(listeKombinator) }

/**
* Extension-metode for å kombinere liste av tidslinjer
* Kombinasjonen baserer seg på å iterere gjennom alle tidspunktene
* fra minste <fraOgMed()> til største <tilOgMed()> fra alle tidslinjene
* Innhold (I) og tidsenhet (T) må være av samme type
* Resultatet er en tidslinje med tidsenhet T og innhold Iterable<I>
*/
fun <V> Collection<Tidslinje<V>>.kombiner() = this.kombinerNullableKombinator { if (it.toList().isNotEmpty()) it else null }

/**
* Extension-metode for å kombinere tre tidslinjer
* Kombinasjonen baserer seg på å iterere gjennom alle tidspunktene
* fra minste <fraOgMed()> til største <tilOgMed()> fra alle tidslinjene
* Tidsenhet (T) må være av samme type
* Hver av tidslinjene kan ha ulik innholdstype, hhv A, B og C
* Kombintor-funksjonen tar inn (nullable) av A, B og C og returner (nullable) R
* Resultatet er en tidslinje med tidsenhet T og innhold R
*/
fun <A, B, C, R> Tidslinje<A>.kombinerMedKunVerdi(
tidslinjeB: Tidslinje<B>,
tidslinjeC: Tidslinje<C>,
kombinator: (A, B, C) -> R?,
): Tidslinje<R> =
this.kombinerMed(tidslinjeB, tidslinjeC) { a, b, c ->
when {
a != null && b != null && c != null -> kombinator(a, b, c)
else -> null
}
}

fun <V> Tidslinje<V>.erIkkeTom() = !this.erTom()

fun <V, H> Tidslinje<V>.harOverlappMed(tidslinje: Tidslinje<H>) = this.kombinerUtenNullMed(tidslinje) { v, h -> true }.erIkkeTom()

fun <V, H> Tidslinje<V>.harIkkeOverlappMed(tidslinje: Tidslinje<H>) = !this.harOverlappMed(tidslinje)

fun <V, H> Tidslinje<V>.kombinerMedNullable(
høyreTidslinje: Tidslinje<H>?,
kombinator: (V?, H?) -> V?,
): Tidslinje<V> =
if (høyreTidslinje != null) {
kombinerMed(høyreTidslinje, kombinator)
} else {
this
}

/**
* Extension-metode for å kombinere liste av tidslinjer
* Kombinasjonen baserer seg på å iterere gjennom alle tidspunktene
* fra minste <fraOgMed()> til største <tilOgMed()> fra alle tidslinjene
* Innhold (I) og tidsenhet (T) må være av samme type
* Kombintor-funksjonen tar inn Iterable<I> og returner (nullable) R
* Resultatet er en tidslinje med tidsenhet T og innhold R
*/
@Deprecated("Skal flyttes til familie-felles")
fun <V, R> Collection<Tidslinje<V>>.kombinerNullableKombinator(listeKombinator: (Iterable<V>) -> R?): Tidslinje<R> =
this.slåSammen().map {
when (it) {
is Verdi -> {
val resultat = listeKombinator(it.verdi)
if (resultat != null) Verdi(resultat) else Null()
}

is Null -> Null()
is Udefinert -> Udefinert()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.komposisjon

import no.nav.familie.ba.sak.common.førsteDagIInneværendeMåned
import no.nav.familie.ba.sak.common.førsteDagINesteMåned
import no.nav.familie.ba.sak.common.sisteDagIForrigeMåned
import no.nav.familie.ba.sak.common.sisteDagIInneværendeMåned
import no.nav.familie.ba.sak.common.sisteDagIMåned
import no.nav.familie.ba.sak.kjerne.grunnlag.personopplysninger.Person
import no.nav.familie.tidslinje.Periode
import no.nav.familie.tidslinje.Tidslinje
import no.nav.familie.tidslinje.tilTidslinje
import java.time.LocalDate
import java.time.YearMonth

fun erUnder18ÅrVilkårTidslinje(fødselsdato: LocalDate): Tidslinje<Boolean> =
opprettBooleanTidslinje(
fraDato = fødselsdato.førsteDagINesteMåned(),
tilDato = fødselsdato.plusYears(18).sisteDagIForrigeMåned(),
)

fun erUnder6ÅrTidslinje(person: Person): Tidslinje<Boolean> =
opprettBooleanTidslinje(
fraDato = person.fødselsdato,
tilDato = person.fødselsdato.plusYears(6).sisteDagIForrigeMåned(),
)

fun erTilogMed3ÅrTidslinje(fødselsdato: LocalDate): Tidslinje<Boolean> =
opprettBooleanTidslinje(
fraDato = fødselsdato,
tilDato = fødselsdato.plusYears(3).sisteDagIMåned(),
)

fun opprettBooleanTidslinje(
fraÅrMåned: YearMonth,
tilÅrMåned: YearMonth,
) = listOf(
Periode(
verdi = true,
fom = fraÅrMåned.førsteDagIInneværendeMåned(),
tom = tilÅrMåned.sisteDagIInneværendeMåned(),
),
).tilTidslinje()

fun opprettBooleanTidslinje(
fraDato: LocalDate,
tilDato: LocalDate,
) = listOf(Periode(verdi = true, fom = fraDato, tom = tilDato)).tilTidslinje()
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.matematikk

import no.nav.familie.ba.sak.kjerne.personident.Aktør
import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.komposisjon.join
import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.komposisjon.kombinerUtenNullOgIkkeTom
import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon.mapIkkeNull
import no.nav.familie.tidslinje.Tidslinje
import java.math.BigDecimal
import java.math.RoundingMode

fun <K> Map<K, Tidslinje<BigDecimal>>.minus(
bTidslinjer: Map<K, Tidslinje<BigDecimal>>,
) = this.join(bTidslinjer) { a, b ->
when {
a != null && b != null -> a - b
else -> a
}
}

fun Map<Aktør, Tidslinje<BigDecimal>>.sum() = values.kombinerUtenNullOgIkkeTom { it.reduce { sum, verdi -> sum.plus(verdi) } }

fun Tidslinje<BigDecimal>.rundAvTilHeltall() = this.mapIkkeNull { it.setScale(0, RoundingMode.HALF_UP) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.matematikk

import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.komposisjon.joinIkkeNull
import no.nav.familie.tidslinje.Tidslinje

fun <K, V : Comparable<V>> minsteAvHver(
aTidslinjer: Map<K, Tidslinje<V>>,
bTidslinjer: Map<K, Tidslinje<V>>,
) = aTidslinjer.joinIkkeNull(bTidslinjer) { a, b -> minOf(a, b) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon

import no.nav.familie.tidslinje.Tidslinje
import no.nav.familie.tidslinje.tomTidslinje
import no.nav.familie.tidslinje.utvidelser.klipp
import java.time.LocalDate

/**
* Extension-metode for å beskjære (forkorte) en tidslinje etter til-og-med fra en annen tidslinje
* Etter beskjæringen vil tidslinjen maksimalt strekke seg fra [this]s fraOgMed() og til [tidslinje]s tilOgMed()
* Perioder som ligger helt utenfor grensene vil forsvinne.
* Perioden i hver ende som ligger delvis innenfor, vil forkortes.
* Hvis ny og eksisterende grenseverdi begge er uendelige, vil den nye benyttes
* Beskjæring mot tom tidslinje vil gi tom tidslinje
*/
fun <I> Tidslinje<I>.beskjærTilOgMedEtter(tidslinje: Tidslinje<*>): Tidslinje<I> =
when {
tidslinje.erTom() -> tomTidslinje()
else ->
klipp(
startsTidspunkt = startsTidspunkt,
sluttTidspunkt = tidslinje.kalkulerSluttTidspunkt(),
)
}

/**
* Extension-metode for å beskjære (forkorte) en tidslinje
* Etter beskjæringen vil tidslinjen maksimalt strekke seg fra innsendt [fraOgMed] og til [tilOgMed]
* Perioder som ligger helt utenfor grensene vil forsvinne.
* Perioden i hver ende som ligger delvis innenfor, vil forkortes.
* Uendelige endepunkter vil beskjæres til endelig hvis [fraOgMed] eller [tilOgMed] er endelige
* Endelige endepunkter som beskjæres mot uendelige endepunkter, beholdes
* Hvis ny og eksisterende grenseverdi begge er uendelige, vil den mest ekstreme benyttes
*/
fun <V> Tidslinje<V>.beskjær(
fraOgMed: LocalDate,
tilOgMed: LocalDate,
): Tidslinje<V> =
when {
erTom() -> tomTidslinje()
else ->
klipp(
startsTidspunkt = fraOgMed,
sluttTidspunkt = tilOgMed,
)
}

/**
* Extension-metode for å beskjære fom dato på en tidslinje
* Etter beskjæringen vil tidslinjen maksimalt strekke seg fra innsendt [fraOgMed] og til eksisterende tilOgMed
*/
fun <V> Tidslinje<V>.beskjærFraOgMed(
fraOgMed: LocalDate,
): Tidslinje<V> =
when {
erTom() -> tomTidslinje()
else ->
klipp(
startsTidspunkt = fraOgMed,
sluttTidspunkt = kalkulerSluttTidspunkt(),
)
}

/**
* Extension-metode for å beskjære tom dato på en tidslinje
* Etter beskjæringen vil tidslinjen maksimalt strekke seg fra eksisterende fraOgMed og til innsendt [tilOgMed]
*/
fun <V> Tidslinje<V>.beskjærTilOgMed(
tilOgMed: LocalDate,
): Tidslinje<V> =
when {
erTom() -> tomTidslinje()
else ->
klipp(
startsTidspunkt = startsTidspunkt,
sluttTidspunkt = tilOgMed,
)
}

/**
* Extension-metode for å beskjære tom dato på et map av tidslinjer
* Etter beskjæringen vil tidslinjen maksimalt strekke seg fra eksisterende fraOgMed og til innsendt [tilOgMed]
*/
fun <K, V> Map<K, Tidslinje<V>>.beskjærTilOgMed(
tilOgMed: LocalDate,
): Map<K, Tidslinje<V>> = this.mapValues { (_, tidslinje) -> tidslinje.beskjærTilOgMed(tilOgMed) }
Loading