diff --git a/pom.xml b/pom.xml index d03aab0e85..12378ee780 100644 --- a/pom.xml +++ b/pom.xml @@ -318,6 +318,10 @@ no.nav.familie.felles metrikker + + no.nav.familie.felles + tidslinje + no.nav.familie.kontrakter felles diff --git a/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeJoin.kt b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeJoin.kt new file mode 100644 index 0000000000..f9e559e38a --- /dev/null +++ b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeJoin.kt @@ -0,0 +1,55 @@ +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 + * Verdiene i tidslinjene i map'en på venstre side må alle være av typen V + * Verdiene 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. + * 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 Map>.join( + høyreTidslinjer: Map>, + kombinator: (V?, H?) -> R?, +): Map> { + 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 + * Verdiene i tidslinjene i map'en på venstre side må alle være av typen V + * Verdiene 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 Map>.joinIkkeNull( + høyreTidslinjer: Map>, + kombinator: (V, H) -> R?, +): Map> { + 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) + } +} diff --git a/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeKombinator.kt b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeKombinator.kt new file mode 100644 index 0000000000..a724f58fe4 --- /dev/null +++ b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeKombinator.kt @@ -0,0 +1,143 @@ +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 +import no.nav.familie.tidslinje.utvidelser.trim + +/** + * Extension-metode for å kombinere to tidslinjer der begge har verdi + * Kombinasjonen baserer seg på å iterere gjennom alle tidspunktene + * Hver av tidslinjene kan ha ulik type, hhv V og H + * Hvis V eller H mangler verdi, så vil ikke resulterende tidslinje få en verdi for det tidspunktet + * Kombintor-funksjonen tar ellers V og H og returnerer (nullable) R + * Hvis kombinator-funksjonen returner , antas det at tidslinjen ikke skal ha verdi for tidspunktet + * Resultatet er en tidslinje med verdi R + */ +fun Tidslinje.kombinerUtenNullMed( + høyreTidslinje: Tidslinje, + kombineringsfunksjon: (V, H) -> R?, +): Tidslinje = + 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 alle tidslinjene + * Verdi (V) må være av samme type + * Kombintor-funksjonen tar inn Iterable og returner (nullable) R + * Null-verdier fjernes før de sendes til kombinator-funksjonen, som betyr at en tom iterator kan bli sendt + * Hvis resultatet fra kombinatoren er null, tolkes det som at det ikke skal være en verdi + * Resultatet er en tidslinje med verdi R + */ +fun Collection>.kombinerUtenNull( + listeKombinator: (Iterable) -> R?, +): Tidslinje = kombinerNullableKombinator { it.filterNotNull().let(listeKombinator) } + +/** + * Extension-metode for å kombinere liste av tidslinjer + * Kombinasjonen baserer seg på å iterere gjennom alle tidspunktene fra alle tidslinjene + * Verdi (V) må være av samme type + * Kombintor-funksjonen tar inn Iterable 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 en verdi + * Resultatet er en tidslinje med verdi R + */ +fun Collection>.kombinerUtenNullOgIkkeTom( + listeKombinator: (Iterable) -> R?, +): Tidslinje = kombinerNullableKombinator { it.filterNotNull().takeIf { it.isNotEmpty() }?.let(listeKombinator) } + +/** + * Extension-metode for å kombinere liste av tidslinjer + * Kombinasjonen baserer seg på å iterere gjennom alle tidspunktene fra alle tidslinjene + * Verdien V må være av samme type + * Resultatet er en tidslinje med verdi Iterable + */ +fun Collection>.kombiner() = this.kombinerNullableKombinator { if (it.toList().isNotEmpty()) it else null } + +/** + * Extension-metode for å kombinere en nøkkel-verdi-map'er der verdiene er tidslinjer, med en enkelt tidslinje + * Verdien i tidslinjene i map'en på venstre side må alle være av typen V + * Verdien 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 + * verdien 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 verdi av typen (nullable) R. + */ +fun Map>.kombinerKunVerdiMed( + høyreTidslinje: Tidslinje, + kombinator: (V, H) -> R?, +): Map> { + val venstreTidslinjer = this + + return venstreTidslinjer.mapValues { (_, venstreTidslinje) -> + venstreTidslinje.kombinerUtenNullMed(høyreTidslinje, kombinator) + } +} + +/** + * Extension-metode for å kombinere tre tidslinjer + * Kombinasjonen baserer seg på å iterere gjennom alle tidspunktene fra alle tidslinjene + * Hver av tidslinjene kan ha ulik type, hhv A, B og C + * Kombintor-funksjonen tar inn av A, B og C og returner (nullable) R + * Resultatet er en tidslinje med verdi R + */ +fun Tidslinje.kombinerKunVerdiMed( + tidslinjeB: Tidslinje, + tidslinjeC: Tidslinje, + kombinator: (A, B, C) -> R?, +): Tidslinje = + this.kombinerMed(tidslinjeB, tidslinjeC) { a, b, c -> + when { + a != null && b != null && c != null -> kombinator(a, b, c) + else -> null + } + } + +fun Tidslinje.erIkkeTom() = !this.erTom() + +fun Tidslinje.harOverlappMed(tidslinje: Tidslinje) = this.kombinerUtenNullMed(tidslinje) { v, h -> true }.trim(Null()).erIkkeTom() + +fun Tidslinje.harIkkeOverlappMed(tidslinje: Tidslinje) = !this.harOverlappMed(tidslinje) + +fun Tidslinje.kombinerMedNullable( + høyreTidslinje: Tidslinje?, + kombinator: (V?, H?) -> V?, +): Tidslinje = + 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 alle tidslinjene + * Verdi (V) må være av samme type + * Kombintor-funksjonen tar inn Iterable og returner (nullable) R + * Resultatet er en tidslinje med verdi R + */ +fun Collection>.kombinerNullableKombinator(listeKombinator: (Iterable) -> R?): Tidslinje = + 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() + } + } diff --git a/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeUtil.kt b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeUtil.kt new file mode 100644 index 0000000000..16c4de84a2 --- /dev/null +++ b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeUtil.kt @@ -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 = + opprettBooleanTidslinje( + fraDato = fødselsdato.førsteDagINesteMåned(), + tilDato = fødselsdato.plusYears(18).sisteDagIForrigeMåned(), + ) + +fun erUnder6ÅrTidslinje(person: Person): Tidslinje = + opprettBooleanTidslinje( + fraDato = person.fødselsdato.førsteDagIInneværendeMåned(), + tilDato = person.fødselsdato.plusYears(6).sisteDagIForrigeMåned(), + ) + +fun erTilogMed3ÅrTidslinje(fødselsdato: LocalDate): Tidslinje = + opprettBooleanTidslinje( + fraDato = fødselsdato.førsteDagINesteMåned(), + 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() diff --git a/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/BigDecimalTidslinje.kt b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/BigDecimalTidslinje.kt new file mode 100644 index 0000000000..b0727dddee --- /dev/null +++ b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/BigDecimalTidslinje.kt @@ -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 Map>.minus( + bTidslinjer: Map>, +) = this.join(bTidslinjer) { a, b -> + when { + a != null && b != null -> a - b + else -> a + } +} + +fun Map>.sum() = values.kombinerUtenNullOgIkkeTom { it.reduce { sum, verdi -> sum.plus(verdi) } } + +fun Tidslinje.rundAvTilHeltall() = this.mapIkkeNull { it.setScale(0, RoundingMode.HALF_UP) } diff --git a/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/SammenliknbarTidslinje.kt b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/SammenliknbarTidslinje.kt new file mode 100644 index 0000000000..436b7f082f --- /dev/null +++ b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/SammenliknbarTidslinje.kt @@ -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 > minsteAvHver( + aTidslinjer: Map>, + bTidslinjer: Map>, +) = aTidslinjer.joinIkkeNull(bTidslinjer) { a, b -> minOf(a, b) } diff --git "a/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/Beskj\303\246reTidslinje.kt" "b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/Beskj\303\246reTidslinje.kt" new file mode 100644 index 0000000000..18a3109486 --- /dev/null +++ "b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/Beskj\303\246reTidslinje.kt" @@ -0,0 +1,84 @@ +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 startTidspunkt og til [tidslinje]s sluttTidspunkt + * 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 Tidslinje.beskjærTilOgMedEtter(tidslinje: Tidslinje<*>): Tidslinje = + 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 + */ +fun Tidslinje.beskjær( + fraOgMed: LocalDate, + tilOgMed: LocalDate, +): Tidslinje = + 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 Tidslinje.beskjærFraOgMed( + fraOgMed: LocalDate, +): Tidslinje = + 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 Tidslinje.beskjærTilOgMed( + tilOgMed: LocalDate, +): Tidslinje = + 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 Map>.beskjærTilOgMed( + tilOgMed: LocalDate, +): Map> = this.mapValues { (_, tidslinje) -> tidslinje.beskjærTilOgMed(tilOgMed) } diff --git a/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/EndreTid.kt b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/EndreTid.kt new file mode 100644 index 0000000000..b2e064dc42 --- /dev/null +++ b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/EndreTid.kt @@ -0,0 +1,90 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon + +import no.nav.familie.tidslinje.Null +import no.nav.familie.tidslinje.Tidslinje +import no.nav.familie.tidslinje.Udefinert +import no.nav.familie.tidslinje.tilPeriodeVerdi +import no.nav.familie.tidslinje.utvidelser.konverterTilDag +import no.nav.familie.tidslinje.utvidelser.konverterTilMåned +import no.nav.familie.tidslinje.utvidelser.trim + +/** + * Extension-metode for å konvertere fra dag-basert tidslinje til måned-basert tidslinje + * mapper-funksjonen tar inn listen av alle dagverdiene i én måned, og returner verdien måneden skal ha + * Dagverdiene kommer i samme rekkefølge som dagene i måneden, og vil ha null-verdi hvis dagen ikke har en verdi + */ +fun Tidslinje.tilMåned(mapper: (List) -> V?): Tidslinje = + this.konverterTilMåned { dato, månedListe -> + val månedVerdier = månedListe.first().map { dag -> dag.periodeVerdi.verdi } + mapper(månedVerdier).tilPeriodeVerdi() + } + +/** + * Extention-metode som konverterer en dag-basert tidslinje til en måned-basert tidslinje. + * -funksjonen tar inn verdiene fra de to dagene før og etter månedsskiftet, + * det vil si verdiene fra siste dag i forrige måned og første dag i inneværemde måned. + * -funksjonen kalles bare dersom begge dagene har en verdi. + * Return-verdien er innholdet som blir brukt for inneværende måned. + * Hvis retur-verdien er , vil den resulterende måneden mangle verdi. + * Funksjonen vil bruke månedsskiftene fra måneden før tidslinjen starter frem til og med siste måned i tidslinjen. + */ +fun Tidslinje.tilMånedFraMånedsskifteIkkeNull( + mapper: (innholdSisteDagForrigeMåned: V, innholdFørsteDagDenneMåned: V) -> V?, +): Tidslinje = + this + .konverterTilMåned(antallMndBakoverITid = 1) { dato, måneder -> + val sisteDagForrigeMåned = + måneder + .first() + .lastOrNull() + ?.periodeVerdi + ?.verdi + val førsteDagDenneMåned = + måneder + .last() + .firstOrNull() + ?.periodeVerdi + ?.verdi + + if (sisteDagForrigeMåned == null || førsteDagDenneMåned == null) { + Null() + } else { + mapper(sisteDagForrigeMåned, førsteDagDenneMåned).tilPeriodeVerdi() + } + }.trim(Null(), Udefinert()) + +/** + * Extention-metode som konverterer en dag-basert tidslinje til en måned-basert tidslinje. + * -funksjonen tar inn verdiene fra de to dagene før og etter månedsskiftet, + * det vil si verdiene fra siste dag i forrige måned og første dag i inneværemde måned. + * Return-verdien er innholdet som blir brukt for inneværende måned. + * Hvis retur-verdien er , vil den resulterende måneden mangle verdi. + * Funksjonen vil bruke månedsskiftene fra måneden før tidslinjen starter frem til og med siste måned i tidslinjen. + */ +fun Tidslinje.tilMånedFraMånedsskifte( + mapper: (innholdSisteDagForrigeMåned: V?, innholdFørsteDagDenneMåned: V?) -> V?, +): Tidslinje = + this + .konverterTilMåned(antallMndBakoverITid = 1) { dato, måneder -> + val sisteDagForrigeMåned = + måneder + .first() + .lastOrNull() + ?.periodeVerdi + ?.verdi + val førsteDagDenneMåned = + måneder + .last() + .firstOrNull() + ?.periodeVerdi + ?.verdi + + mapper(sisteDagForrigeMåned, førsteDagDenneMåned).tilPeriodeVerdi() + } + +/** + * Extension-metode for å konvertere fra Måned-tidslinje til Dag-tidslinje + * Første dag i fra-og-med-måneden brukes som første dag i perioden + * Siste dag i til-og-med-måneden brukes som siste dag i perioden + */ +fun Tidslinje.tilDag(): Tidslinje = this.konverterTilDag() diff --git a/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/FiltrerTidslinje.kt b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/FiltrerTidslinje.kt new file mode 100644 index 0000000000..25ed24b2df --- /dev/null +++ b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/FiltrerTidslinje.kt @@ -0,0 +1,30 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon + +import no.nav.familie.tidslinje.Tidslinje +import no.nav.familie.tidslinje.beskjærEtter +import no.nav.familie.tidslinje.utvidelser.filtrer +import no.nav.familie.tidslinje.utvidelser.kombinerMed + +fun Tidslinje.filtrerIkkeNull(filter: (V) -> Boolean): Tidslinje = filtrer { it != null && filter(it) } + +/** + * Extension-metode for å filtrere tidslinjen mot en boolsk tidslinje + * Resultatet får samme lengde som tidslinjen det opereres på + * Det vil finnes perioder som tilsvarer periodene fra kilde-tidslinjen, + * men innholdet blir null hvis den boolske tidslinjen er false + */ +fun Tidslinje.filtrerMed(boolskTidslinje: Tidslinje): Tidslinje = + this + .kombinerMed(boolskTidslinje) { verdi, erSann -> + when (erSann) { + true -> verdi + else -> null + } + }.beskjærEtter(this) + +/** + * Extension-metode for å filtrere innholdet i en map av tidslinjer + */ +fun Map>.filtrerHverKunVerdi( + filter: (V) -> Boolean, +) = mapValues { (_, tidslinje) -> tidslinje.filtrer { if (it != null) filter(it) else false } } diff --git a/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/MapTidslinje.kt b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/MapTidslinje.kt new file mode 100644 index 0000000000..8205e78de6 --- /dev/null +++ b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/MapTidslinje.kt @@ -0,0 +1,16 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon + +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.utvidelser.map + +fun Tidslinje.mapIkkeNull(mapper: (V) -> R?): Tidslinje = + this.map { periodeVerdi -> + when (periodeVerdi) { + is Verdi -> mapper(periodeVerdi.verdi)?.let { Verdi(it) } ?: Null() + is Null -> Null() + is Udefinert -> Udefinert() + } + } diff --git a/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/Zip.kt b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/Zip.kt new file mode 100644 index 0000000000..4006e8b00e --- /dev/null +++ b/src/main/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/Zip.kt @@ -0,0 +1,40 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon + +import no.nav.familie.tidslinje.Periode +import no.nav.familie.tidslinje.Tidslinje +import no.nav.familie.tidslinje.tilTidslinje +import no.nav.familie.tidslinje.utvidelser.tilPerioder + +/** + * Returnerer en tidslinje med par av hvert etterfølgende element i tidslinjen. + * + * val aTilD = "abcd" + * val bokstavTidslinje = aTilF.tilCharTidslinje(jan(2020)) + * val bokstavParTidslinje = bokstavTidslinje.zipMedNeste(ZipPadding.FØR) + * + * println(bokstavTidslinje) // + * 2020-01 - 2020-01: a | 2020-02 - 2020-02: b | 2020-03 - 2020-03: c | 2020-04 - 2020-04: d + * + * println(bokstavParTidslinje) // + * 2020-01 - 2020-01: (null, a) | 2020-02 - 2020-02: (a, b) | 2020-03 - 2020-03: (b, c) | 2020-04 - 2020-04: (c, d) + */ +enum class ZipPadding { + FØR, + ETTER, + INGEN_PADDING, +} + +fun Tidslinje.zipMedNeste(zipPadding: ZipPadding = ZipPadding.INGEN_PADDING): Tidslinje> { + val padding = listOf(Periode(null, null, null)) + + return when (zipPadding) { + ZipPadding.FØR -> padding + tilPerioder() + ZipPadding.ETTER -> tilPerioder() + padding + ZipPadding.INGEN_PADDING -> tilPerioder() + }.zipWithNext { forrige, denne -> + val verdi = forrige.verdi to denne.verdi + val fom = if (zipPadding == ZipPadding.ETTER) forrige.fom else denne.fom + val tom = if (zipPadding == ZipPadding.ETTER) forrige.tom else denne.tom + Periode(verdi, fom, tom) + }.tilTidslinje() +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/TidslinjeKombinasjonTest.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/TidslinjeKombinasjonTest.kt new file mode 100644 index 0000000000..348b069038 --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/TidslinjeKombinasjonTest.kt @@ -0,0 +1,87 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles + +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jan +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilCharTidslinje +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilStringTidslinje +import no.nav.familie.tidslinje.utvidelser.kombinerMed +import no.nav.familie.tidslinje.utvidelser.tilPerioder +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class TidslinjeKombinasjonTest { + private val kombinator = { venstre: Char?, høyre: Char? -> + (venstre?.toString() ?: "").trim() + (høyre?.toString() ?: "").trim() + } + + @Test + fun testEndeligeLikeLangTidslinjer() { + assertTidslinjer( + linje1 = "abcdef", + linje2 = "fedcba", + "af", + "be", + "cd", + "dc", + "eb", + "fa", + ) + } + + @Test + fun testEndeligeTidslinjerMedForskjelligLengde() { + assertTidslinjer( + linje1 = " ab", + linje2 = "fedcba", + "f", + "e", + "ad", + "bc", + "b", + "a", + ) + } + + @Test + fun testUendeligeTidslinjerFremover() { + assertTidslinjer( + linje1 = "abc>", + linje2 = "abacd>", + "aa", + "bb", + "ca", + "cc", + "cd", + ">", + ) + } + + @Test + fun testUendeligeTidslinjerBeggeVeier() { + assertTidslinjer( + linje1 = "", + ) + } + + private fun assertTidslinjer( + linje1: String, + linje2: String, + vararg forventet: String, + ) { + val fom = jan(2020) + val char1 = linje1.tilCharTidslinje(fom) + val char2 = linje2.tilCharTidslinje(fom) + + val kombinertePerioder = char1.kombinerMed(char2, kombinator).tilPerioder() + val forventedePerioder = forventet.toList().tilStringTidslinje(fom).tilPerioder() + + Assertions.assertEquals(forventedePerioder, kombinertePerioder) + } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeJoinTest.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeJoinTest.kt new file mode 100644 index 0000000000..7243af287d --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeJoinTest.kt @@ -0,0 +1,62 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.komposisjon + +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.apr +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.feb +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jan +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.mar +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilCharTidslinje +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilStringTidslinje +import no.nav.familie.tidslinje.utvidelser.tilPerioder +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class TidslinjeJoinTest { + private val tidslinje1 = mapOf("key" to "abc".tilCharTidslinje(jan(2000))) + private val tidslinje2 = mapOf("key" to "fgh".tilCharTidslinje(feb(2000))) + + @Test + fun join() { + val faktiskePerioder = + tidslinje1 + .join(tidslinje2) { c1, c2 -> + when { + c1 == null && c2 == null -> null + c1 == null -> c2.toString() + c2 == null -> c1.toString() + else -> c1.toString() + c2.toString() + } + }["key"]!! + + val forventedePerioder = listOf("a", "bf", "cg", "h").tilStringTidslinje(jan(2000)) + + assertThat(faktiskePerioder).isEqualTo(forventedePerioder) + } + + @Test + fun joinIkkeNull() { + val faktiskePerioder = + tidslinje1 + .joinIkkeNull(tidslinje2) { c1, c2 -> + c1.toString() + c2.toString() + }["key"]!! + .tilPerioder() + + assertThat(faktiskePerioder).hasSize(4) + + assertThat(faktiskePerioder[0].verdi).isNull() + assertThat(faktiskePerioder[0].fom).isEqualTo(1.jan(2000)) + assertThat(faktiskePerioder[0].tom).isEqualTo(31.jan(2000)) + + assertThat(faktiskePerioder[1].verdi).isEqualTo("bf") + assertThat(faktiskePerioder[1].fom).isEqualTo(1.feb(2000)) + assertThat(faktiskePerioder[1].tom).isEqualTo(29.feb(2000)) + + assertThat(faktiskePerioder[2].verdi).isEqualTo("cg") + assertThat(faktiskePerioder[2].fom).isEqualTo(1.mar(2000)) + assertThat(faktiskePerioder[2].tom).isEqualTo(31.mar(2000)) + + assertThat(faktiskePerioder[3].verdi).isNull() + assertThat(faktiskePerioder[3].fom).isEqualTo(1.apr(2000)) + assertThat(faktiskePerioder[3].tom).isEqualTo(30.apr(2000)) + } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeKombinatorTest.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeKombinatorTest.kt new file mode 100644 index 0000000000..c4482e68c2 --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeKombinatorTest.kt @@ -0,0 +1,120 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.komposisjon + +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.feb +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jan +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilCharTidslinje +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilStringTidslinje +import no.nav.familie.tidslinje.Tidslinje +import no.nav.familie.tidslinje.tomTidslinje +import no.nav.familie.tidslinje.utvidelser.tilPerioder +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class TidslinjeKombinatorTest { + /* + Dager: | 1. januar | 2. januar | 3. januar | 4. januar | 5. januar | + Tidslinje 1: | a | a | a | a | + Tidslinje 2: | b | b | b | + Tidslinje 3: | c | null | null | c | + */ + private val tidslinje1 = "aaaa".tilCharTidslinje(1.jan(2000)) + private val tidslinje2 = "bbb".tilCharTidslinje(3.jan(2000)) + private val tidslinje3 = "c c".tilCharTidslinje(1.jan(2000)) + + @Test + fun kombinerUtenNull() { + val faktisk = listOf(tidslinje1, tidslinje2, tidslinje3).kombinerUtenNull { it.joinToString("") } + val forventet = listOf("ac", "a", "ab", "abc", "b").tilStringTidslinje(1.jan(2000)) + + assertThat(faktisk).isEqualTo(forventet) + } + + @Test + fun kombinerUtenNullOgIkkeTom() { + val faktisk = listOf(tidslinje1, tidslinje2, tidslinje3).kombinerUtenNullOgIkkeTom { it.joinToString("") } + val forventet = listOf("ac", "a", "ab", "abc", "b").tilStringTidslinje(1.jan(2000)) + + assertThat(faktisk).isEqualTo(forventet) + } + + @Test + fun kombiner() { + val faktisk = listOf(tidslinje1, tidslinje2, tidslinje3).kombiner().tilPerioder() + + assertThat(faktisk.size).isEqualTo(5) + assertThat(faktisk[0].verdi).containsExactly('a', 'c') + assertThat(faktisk[1].verdi).containsExactly('a') + assertThat(faktisk[2].verdi).containsExactly('a', 'b') + assertThat(faktisk[3].verdi).containsExactly('a', 'b', 'c') + assertThat(faktisk[4].verdi).containsExactly('b') + } + + @Test + fun kombinerKunVerdiMedMap() { + val tidslinjeMap = mapOf("key1" to tidslinje1, "key2" to tidslinje2) + + val faktisk = + tidslinjeMap.kombinerKunVerdiMed(tidslinje3) { c1, c2 -> + c1.toString() + c2.toString() + } + + val forventet1 = listOf("ac", null, null, "ac").tilStringTidslinje(1.jan(2000)) + val forventet2 = listOf(null, null, null, "bc", null).tilStringTidslinje(1.jan(2000)) + + assertThat(faktisk["key1"]).isEqualTo(forventet1) + assertThat(faktisk["key2"]).isEqualTo(forventet2) + } + + @Test + fun kombinerKunVerdiMed() { + val faktisk = + tidslinje1.kombinerKunVerdiMed(tidslinje2, tidslinje3) { c1, c2, c3 -> + c1.toString() + c2.toString() + c3.toString() + } + + val forventet = listOf(null, null, null, "abc", null).tilStringTidslinje(1.jan(2000)) + + assertThat(faktisk).isEqualTo(forventet) + } + + @Test + fun erIkkeTom() { + assertThat(tomTidslinje().erIkkeTom()).isFalse() + assertThat("a".tilCharTidslinje(1.jan(2000)).erIkkeTom()).isTrue() + } + + @Test + fun harOverlappMed() { + assertThat(tidslinje1.harOverlappMed(tidslinje2)).isTrue() + + val tidslinjeUtenOverlappMedTidslinje1 = "a".tilCharTidslinje(1.feb(2000)) + assertThat(tidslinje1.harOverlappMed(tidslinjeUtenOverlappMedTidslinje1)).isFalse() + } + + @Test + fun harIkkeOverlappMed() { + assertThat(tidslinje1.harIkkeOverlappMed(tidslinje2)).isFalse() + + val tidslinjeUtenOverlappMedTidslinje1 = "a".tilCharTidslinje(1.feb(2000)) + assertThat(tidslinje1.harIkkeOverlappMed(tidslinjeUtenOverlappMedTidslinje1)).isTrue() + } + + @Test + fun `kombinerMedNullable skal returnere this hvis den andre tidslinjen er null`() { + val nullTidslinje: Tidslinje? = null + val faktisk = tidslinje1.kombinerMedNullable(nullTidslinje) { c1, c2 -> c1 } + assertThat(faktisk).isEqualTo(tidslinje1) + } + + @Test + fun kombinerMedNullable() { + val faktisk = + tidslinje1.kombinerMedNullable(tidslinje2) { c1, c2 -> + if (c1 == null || c2 == null) null else c1 + } + + val forventet = " aa ".tilCharTidslinje(1.jan(2000)) + + assertThat(faktisk).isEqualTo(forventet) + } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeUtilTest.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeUtilTest.kt new file mode 100644 index 0000000000..9f8ba2643f --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/komposisjon/TidslinjeUtilTest.kt @@ -0,0 +1,69 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.komposisjon + +import no.nav.familie.ba.sak.datagenerator.lagPerson +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.des +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.feb +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jan +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.mar +import no.nav.familie.tidslinje.utvidelser.tilPerioder +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class TidslinjeUtilTest { + private val person = lagPerson(fødselsdato = 15.jan(2020)) + + @Test + fun erUnder18ÅrVilkårTidslinje() { + val erUnder18ÅrVilkårTidslinje = erUnder18ÅrVilkårTidslinje(person.fødselsdato) + val perioder = erUnder18ÅrVilkårTidslinje.tilPerioder() + + assertThat(perioder).hasSize(1) + assertThat(perioder[0].verdi).isTrue() + assertThat(perioder[0].fom).isEqualTo(1.feb(2020)) + assertThat(perioder[0].tom).isEqualTo(31.des(2037)) + } + + @Test + fun erUnder6ÅrTidslinje() { + val erUnder6ÅrTidslinje = erUnder6ÅrTidslinje(person) + val perioder = erUnder6ÅrTidslinje.tilPerioder() + + assertThat(perioder).hasSize(1) + assertThat(perioder[0].verdi).isTrue() + assertThat(perioder[0].fom).isEqualTo(1.jan(2020)) + assertThat(perioder[0].tom).isEqualTo(31.des(2025)) + } + + @Test + fun erTilogMed3ÅrTidslinje() { + val erUnder6ÅrTidslinje = erTilogMed3ÅrTidslinje(person.fødselsdato) + val perioder = erUnder6ÅrTidslinje.tilPerioder() + + assertThat(perioder).hasSize(1) + assertThat(perioder[0].verdi).isTrue() + assertThat(perioder[0].fom).isEqualTo(1.feb(2020)) + assertThat(perioder[0].tom).isEqualTo(31.jan(2023)) + } + + @Test + fun opprettBooleanTidslinjeYearMonth() { + val tidslinje = opprettBooleanTidslinje(jan(2020), mar(2020)) + val perioder = tidslinje.tilPerioder() + + assertThat(perioder).hasSize(1) + assertThat(perioder[0].verdi).isTrue() + assertThat(perioder[0].fom).isEqualTo(1.jan(2020)) + assertThat(perioder[0].tom).isEqualTo(31.mar(2020)) + } + + @Test + fun opprettBooleanTidslinjeLocalDate() { + val tidslinje = opprettBooleanTidslinje(15.jan(2020), 15.mar(2020)) + val perioder = tidslinje.tilPerioder() + + assertThat(perioder).hasSize(1) + assertThat(perioder[0].verdi).isTrue() + assertThat(perioder[0].fom).isEqualTo(15.jan(2020)) + assertThat(perioder[0].tom).isEqualTo(15.mar(2020)) + } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/BigDecimalTidslinjeTest.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/BigDecimalTidslinjeTest.kt new file mode 100644 index 0000000000..221b5ef520 --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/BigDecimalTidslinjeTest.kt @@ -0,0 +1,62 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.matematikk + +import no.nav.familie.ba.sak.datagenerator.randomAktør +import no.nav.familie.tidslinje.Periode +import no.nav.familie.tidslinje.tilTidslinje +import no.nav.familie.tidslinje.utvidelser.tilPerioder +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.math.BigDecimal +import java.time.LocalDate + +class BigDecimalTidslinjeTest { + private val tidslinje1 = + listOf(BigDecimal(1.5), BigDecimal(2.5), BigDecimal(4)) + .mapIndexed { index, verdi -> + Periode( + verdi = verdi, + fom = LocalDate.now().plusDays(index.toLong()), + tom = LocalDate.now().plusDays(index.toLong()), + ) + }.tilTidslinje() + + private val tidslinje2 = + listOf(BigDecimal(10), BigDecimal(10), BigDecimal(10)) + .mapIndexed { index, verdi -> + Periode( + verdi = verdi, + fom = LocalDate.now().plusDays(index.toLong()), + tom = LocalDate.now().plusDays(index.toLong()), + ) + }.tilTidslinje() + + private val tidslinjeMap = + mapOf( + randomAktør() to tidslinje1, + randomAktør() to tidslinje2, + ) + + @Test + fun minus() { + val aktør = randomAktør() + val tidslinje1AsMap = mapOf(aktør to tidslinje1) + val tidslinje2AsMap = mapOf(aktør to tidslinje2) + val subtrahertePerioder = tidslinje2AsMap.minus(tidslinje1AsMap)[aktør]!!.tilPerioder().map { it.verdi } + + assertThat(subtrahertePerioder).containsExactly(BigDecimal(8.5), BigDecimal(7.5), BigDecimal(6)) + } + + @Test + fun sum() { + val summertePerioder = tidslinjeMap.sum().tilPerioder() + + assertThat(summertePerioder.map { it.verdi }).containsExactly(BigDecimal(11.5), BigDecimal(12.5), BigDecimal(14)) + } + + @Test + fun rundAvTilHeleTall() { + val avrundedeTall = tidslinje1.rundAvTilHeltall().tilPerioder().map { it.verdi } + + assertThat(avrundedeTall).containsExactly(BigDecimal(2), BigDecimal(3), BigDecimal(4)) + } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/SammenliknbarTidslinjeTest.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/SammenliknbarTidslinjeTest.kt new file mode 100644 index 0000000000..69d7e458c0 --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/matematikk/SammenliknbarTidslinjeTest.kt @@ -0,0 +1,19 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.matematikk + +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jan +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilCharTidslinje +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class SammenliknbarTidslinjeTest { + @Test + fun minsteAvHver() { + val tidslinje1 = mapOf(1 to "acegi".tilCharTidslinje(jan(2000))) + val tidslinje2 = mapOf(1 to "bbddi".tilCharTidslinje(jan(2000))) + + val minsteAvHver = minsteAvHver(tidslinje1, tidslinje2)[1]!! + val forventetTidslinje = "abddi".tilCharTidslinje(jan(2000)) + + assertThat(minsteAvHver).isEqualTo(forventetTidslinje) + } +} diff --git "a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/Beskj\303\246reTidslinjeTest.kt" "b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/Beskj\303\246reTidslinjeTest.kt" new file mode 100644 index 0000000000..2ad01abcaa --- /dev/null +++ "b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/Beskj\303\246reTidslinjeTest.kt" @@ -0,0 +1,225 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon + +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.apr +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.des +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.feb +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jan +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.mar +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilCharTidslinje +import no.nav.familie.tidslinje.TidsEnhet.MÅNED +import no.nav.familie.tidslinje.beskjærEtter +import no.nav.familie.tidslinje.tomTidslinje +import no.nav.familie.tidslinje.utvidelser.tilPerioder +import org.junit.Assert.assertEquals +import org.junit.jupiter.api.Test + +internal class BeskjæreTidslinjeTest { + @Test + fun `skal beskjære endelig tidslinje på begge sider`() { + val hovedlinje = "aaaaaa".tilCharTidslinje(des(2001)) + val beskjæring = "bbb".tilCharTidslinje(feb(2002)) + + val faktiskePerioder = hovedlinje.beskjærEtter(beskjæring).tilPerioder() + val forventedePerioder = "aaa".tilCharTidslinje(feb(2002)).tilPerioder() + + assertEquals(forventedePerioder, faktiskePerioder) + } + + @Test + fun `skal beholde tidslinje som allerede er innenfor beskjæring`() { + val hovedlinje = "aaa".tilCharTidslinje(feb(2002)) + val beskjæring = "bbbbbbbbb".tilCharTidslinje(des(2001)) + + val faktiskePerioder = hovedlinje.beskjærEtter(beskjæring).tilPerioder() + val forventedePerioder = "aaa".tilCharTidslinje(feb(2002)).tilPerioder() + + assertEquals(forventedePerioder, faktiskePerioder) + } + + @Test + fun `skal beholde tidslinje som er innenfor en uendelig beskjæring`() { + val hovedlinje = "aaa".tilCharTidslinje(feb(2002)) + val beskjæring = "".tilCharTidslinje(mar(2002)) + + val faktiskePerioder = hovedlinje.beskjærEtter(beskjæring).tilPerioder() + val forventedePerioder = "aaa".tilCharTidslinje(feb(2002)).tilPerioder() + + assertEquals(forventedePerioder, faktiskePerioder) + } + + @Test + fun `beskjæring utenfor tidslinjen skal gi tom tidslinje`() { + val hovedlinje = "aaaaaa".tilCharTidslinje(des(2001)) + val beskjæring = "bbb".tilCharTidslinje(feb(2009)) + + val faktiskePerioder = hovedlinje.beskjærEtter(beskjæring).tilPerioder() + val forventedePerioder = tomTidslinje(beskjæring.startsTidspunkt, MÅNED).tilPerioder() + + assertEquals(forventedePerioder, faktiskePerioder) + } + + @Test + fun `skal beskjære uendelig tidslinje begge veier mot endelig tidsline`() { + val hovedlinje = "".tilCharTidslinje(des(2002)) + val beskjæring = "bbb".tilCharTidslinje(feb(2002)) + + val faktiskePerioder = hovedlinje.beskjærEtter(beskjæring).tilPerioder() + val forventedePerioder = "aaa".tilCharTidslinje(feb(2002)).tilPerioder() + + assertEquals(forventedePerioder, faktiskePerioder) + } + + @Test + fun `skal beskjære tidslinje som går fra uendelig lenge siden til et endelig tidspunkt i fremtiden`() { + val hovedlinje = "()).tilPerioder() + val forventedePerioder = tomTidslinje(tidsEnhet = MÅNED).tilPerioder() + + assertEquals(forventedePerioder, faktiskePerioder) + } + + @Test + fun beskjærTilOgMedEtterTomTidslinje() { + val hovedlinje = "aaaaa".tilCharTidslinje(jan(2000)) + val beskjæring = tomTidslinje() + + val faktiskePerioder = hovedlinje.beskjærTilOgMedEtter(beskjæring).tilPerioder() + val forventedePerioder = tomTidslinje().tilPerioder() + + assertEquals(forventedePerioder, faktiskePerioder) + } + + @Test + fun beskjærTilOgMedEtter() { + val hovedlinje = "aaaaa".tilCharTidslinje(jan(2000)) + val beskjæring = "bbb".tilCharTidslinje(feb(2000)) + + val faktiskePerioder = hovedlinje.beskjærTilOgMedEtter(beskjæring).tilPerioder() + val forventedePerioder = "aaaa".tilCharTidslinje(jan(2000)).tilPerioder() + + assertEquals(forventedePerioder, faktiskePerioder) + } + + @Test + fun beskjær() { + val hovedlinje = "aaaaa".tilCharTidslinje(jan(2000)) + val fom = 1.feb(2000) + val tom = 30.apr(2000) + + val faktiskePerioder = hovedlinje.beskjær(fom, tom).tilPerioder() + val forventedePerioder = "aaa".tilCharTidslinje(feb(2000)).tilPerioder() + + assertEquals(forventedePerioder, faktiskePerioder) + } + + @Test + fun beskjærFom() { + val hovedlinje = "aaaaa".tilCharTidslinje(jan(2000)) + val fom = 1.feb(2000) + + val faktiskePerioder = hovedlinje.beskjærFraOgMed(fom).tilPerioder() + val forventedePerioder = "aaaa".tilCharTidslinje(feb(2000)).tilPerioder() + + assertEquals(forventedePerioder, faktiskePerioder) + } + + @Test + fun beskjærTom() { + val hovedlinje = "aaaaa".tilCharTidslinje(jan(2000)) + val tom = 30.apr(2000) + + val faktiskePerioder = hovedlinje.beskjærTilOgMed(tom).tilPerioder() + val forventedePerioder = "aaaa".tilCharTidslinje(jan(2000)).tilPerioder() + + assertEquals(forventedePerioder, faktiskePerioder) + } + + @Test + fun beskjærTomMap() { + val hovedlinje1 = 1 to "aaaaa".tilCharTidslinje(jan(2000)) + val hovedlinje2 = 2 to "aaaaa".tilCharTidslinje(feb(2000)) + val tidslinjeMap = mapOf(hovedlinje1, hovedlinje2) + val tom = 30.apr(2000) + + val faktiskePerioder = tidslinjeMap.beskjærTilOgMed(tom) + val forventedePerioder1 = "aaaa".tilCharTidslinje(jan(2000)).tilPerioder() + val forventedePerioder2 = "aaa".tilCharTidslinje(feb(2000)).tilPerioder() + + assertEquals(forventedePerioder1, faktiskePerioder[1]!!.tilPerioder()) + assertEquals(forventedePerioder2, faktiskePerioder[2]!!.tilPerioder()) + } + + @Test + fun `beskjæring av tom tidslinje skal gi tom tidslinje`() { + val hovedlinje = tomTidslinje() + val beskjæring = "aaa".tilCharTidslinje(jan(2000)) + val forventedePerioder = tomTidslinje().tilPerioder() + + val faktiskePerioder = + listOf( + hovedlinje.beskjærEtter(beskjæring).tilPerioder(), + hovedlinje.beskjærTilOgMedEtter(beskjæring).tilPerioder(), + hovedlinje.beskjærTilOgMed(beskjæring.startsTidspunkt).tilPerioder(), + hovedlinje.beskjærFraOgMed(beskjæring.kalkulerSluttTidspunkt()).tilPerioder(), + hovedlinje.beskjær(beskjæring.startsTidspunkt, beskjæring.kalkulerSluttTidspunkt()).tilPerioder(), + ) + + faktiskePerioder.forEach { assertEquals(forventedePerioder, it) } + } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/EndreTid.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/EndreTid.kt new file mode 100644 index 0000000000..0655ea94fb --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/EndreTid.kt @@ -0,0 +1,46 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon + +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.feb +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jan +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilCharTidslinje +import no.nav.familie.tidslinje.utvidelser.tilPerioder +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class EndreTid { + @Test + fun tilMåned() { + val tidslinje = "abcdef".tilCharTidslinje(29.jan(2000)) // Månedskille mellom c og d + val månedPerioder = tidslinje.tilMåned { it.filterNotNull().max() }.tilPerioder() + + assertThat(månedPerioder.size).isEqualTo(2) + + assertThat(månedPerioder[0].verdi).isEqualTo('c') + assertThat(månedPerioder[0].fom).isEqualTo(1.jan(2000)) + assertThat(månedPerioder[0].tom).isEqualTo(31.jan(2000)) + + assertThat(månedPerioder[1].verdi).isEqualTo('f') + assertThat(månedPerioder[1].fom).isEqualTo(1.feb(2000)) + assertThat(månedPerioder[1].tom).isEqualTo(29.feb(2000)) + } + + @Test + fun tilMånedFraMånedskifte() { + val tidslinje = "abcdef".tilCharTidslinje(29.jan(2000)) // Månedskille mellom c og d + val månedPerioder = + tidslinje + .tilMånedFraMånedsskifte { innholdSisteDagForrigeMåned, innholdFørsteDagDenneMåned -> + innholdSisteDagForrigeMåned ?: innholdFørsteDagDenneMåned + }.tilPerioder() + + assertThat(månedPerioder.size).isEqualTo(2) + + assertThat(månedPerioder[0].verdi).isEqualTo('a') + assertThat(månedPerioder[0].fom).isEqualTo(1.jan(2000)) + assertThat(månedPerioder[0].tom).isEqualTo(31.jan(2000)) + + assertThat(månedPerioder[1].verdi).isEqualTo('c') + assertThat(månedPerioder[1].fom).isEqualTo(1.feb(2000)) + assertThat(månedPerioder[1].tom).isEqualTo(29.feb(2000)) + } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/FiltrerTidslinjeTest.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/FiltrerTidslinjeTest.kt new file mode 100644 index 0000000000..2e33c48de8 --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/FiltrerTidslinjeTest.kt @@ -0,0 +1,57 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon + +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.apr +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.feb +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jan +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jul +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jun +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.mai +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.mar +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.somBoolskTidslinje +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilCharTidslinje +import no.nav.familie.tidslinje.Periode +import no.nav.familie.tidslinje.utvidelser.tilPerioder +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class FiltrerTidslinjeTest { + @Test + fun filtrerIkkeNull() { + val tidslinje = "aa cc".tilCharTidslinje(feb(2000)) + val opprinneligePerioder = tidslinje.tilPerioder() + + assertThat(opprinneligePerioder[0]).isEqualTo(Periode('a', 1.feb(2000), 31.mar(2000))) + assertThat(opprinneligePerioder[1]).isEqualTo(Periode(null, 1.apr(2000), 31.mai(2000))) + assertThat(opprinneligePerioder[2]).isEqualTo(Periode('c', 1.jun(2000), 31.jul(2000))) + + val filtrertePerioder = tidslinje.filtrerIkkeNull { it == 'a' }.tilPerioder() + + assertThat(filtrertePerioder[0]).isEqualTo(Periode('a', 1.feb(2000), 31.mar(2000))) + assertThat(filtrertePerioder[1]).isEqualTo(Periode(null, 1.apr(2000), 31.jul(2000))) + } + + @Test + fun filtrerMed() { + val tidslinje = "aaaaa".tilCharTidslinje(feb(2000)) + val boolskTidslinje = "ftftftf".somBoolskTidslinje(jan(2000)) + + val filtrertePerioder = tidslinje.filtrerMed(boolskTidslinje).tilPerioder() + + assertThat(filtrertePerioder.map { it.verdi }).containsExactly('a', null, 'a', null, 'a') + } + + @Test + fun filtrerHverKunVerdi() { + val tidslinje1 = "ababa".tilCharTidslinje(jan(2000)) + val tidslinje2 = "babab".tilCharTidslinje(jan(2000)) + + val filtrerteTidslinjer = + mapOf( + "1" to tidslinje1, + "2" to tidslinje2, + ).filtrerHverKunVerdi { it == 'a' } + + assertThat(filtrerteTidslinjer["1"]?.tilPerioder()?.map { it.verdi }).containsExactly('a', null, 'a', null, 'a') + assertThat(filtrerteTidslinjer["2"]?.tilPerioder()?.map { it.verdi }).containsExactly(null, 'a', null, 'a', null) + } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/MapTidslinjeTest.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/MapTidslinjeTest.kt new file mode 100644 index 0000000000..339c251f40 --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/MapTidslinjeTest.kt @@ -0,0 +1,22 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon + +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jan +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilCharTidslinje +import no.nav.familie.tidslinje.mapVerdi +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +internal class MapTidslinjeTest { + @Test + fun `skal mappe innhold og fjerne null`() { + val tidslinje = " AAA BB CCC ".tilCharTidslinje(1.jan(2020)) + + val faktisk = tidslinje.mapIkkeNull { it.lowercase() } + + val forventet = " aaa bb ccc ".tilCharTidslinje(1.jan(2020)).mapVerdi { it?.toString() } + + assertEquals(forventet, faktisk) + assertEquals(1.jan(2020), faktisk.startsTidspunkt) + assertEquals(16.jan(2020), faktisk.kalkulerSluttTidspunkt()) + } +} diff --git "a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/M\303\245nedFraM\303\245nedsskifteTest.kt" "b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/M\303\245nedFraM\303\245nedsskifteTest.kt" new file mode 100644 index 0000000000..125ddb4a9e --- /dev/null +++ "b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/M\303\245nedFraM\303\245nedsskifteTest.kt" @@ -0,0 +1,85 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon + +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.des +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.feb +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.jan +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.nov +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilCharTidslinje +import no.nav.familie.tidslinje.TidsEnhet +import no.nav.familie.tidslinje.tomTidslinje +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +internal class MånedFraMånedsskifteTest { + @Test + fun `skal gi tom tidslinje hvis alle dager er inni én måned`() { + val dagTidslinje = "aaaaaa".tilCharTidslinje(7.des(2021)) + val månedTidslinje = dagTidslinje.tilMånedFraMånedsskifteIkkeNull { _, _ -> 'b' } + + val forventet = tomTidslinje(1.jan(2022), tidsEnhet = TidsEnhet.MÅNED) + + assertEquals(forventet, månedTidslinje) + } + + @Test + fun `skal gi én måned ved ett månedsskifte`() { + val dagTidslinje = "abcdefg".tilCharTidslinje(28.nov(2021)) + val månedTidslinje = + dagTidslinje + .tilMånedFraMånedsskifteIkkeNull { _, verdiFørsteDagDenneMåned -> + verdiFørsteDagDenneMåned + } + + assertEquals("d".tilCharTidslinje(des(2021)), månedTidslinje) + } + + @Test + fun `skal gi to måneder ved to månedsskifter`() { + val dagTidslinje = "abcdefghijklmnopqrstuvwxyzæøå0123456789".tilCharTidslinje(28.nov(2021)) + val månedTidslinje = + dagTidslinje + .tilMånedFraMånedsskifteIkkeNull { verdiSisteDagForrigeMåned, _ -> + verdiSisteDagForrigeMåned + } + + assertEquals("c4".tilCharTidslinje(des(2021)), månedTidslinje) + } + + @Test + fun `skal gi tom tidslinje hvis månedsskiftet mangler verdi på begge sider`() { + val dagTidslinje = + "abcdefghijklmnopqrstuvwxyzæøå0123456789" + .tilCharTidslinje(28.nov(2021)) + .mapIkkeNull { + when (it) { + 'c', 'd', '4', '5' -> null // 30/11, 1/12, 31/12 og 1/1 mangler verdi + else -> it + } + } + + val månedTidslinje = dagTidslinje.tilMånedFraMånedsskifteIkkeNull { _, _ -> 'A' } + + val forventet = tomTidslinje(1.feb(2022), tidsEnhet = TidsEnhet.MÅNED) + + assertEquals(forventet, månedTidslinje) + } + + @Test + fun `skal gi tom tidslinje hvis månedsskiftet mangler verdi på én av sidene`() { + val dagTidslinje = + "abcdefghijklmnopqrstuvwxyzæøå0123456789" + .tilCharTidslinje(28.nov(2021)) + .mapIkkeNull { + when (it) { + 'c', '5' -> null // 30/11 og 1/1 mangler verdi + else -> it + } + } + + val månedTidslinje = dagTidslinje.tilMånedFraMånedsskifteIkkeNull { _, _ -> 'A' } + + val forventet = tomTidslinje(1.feb(2022), tidsEnhet = TidsEnhet.MÅNED) + + assertEquals(forventet, månedTidslinje) + } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/ZipTidslinjeTest.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/ZipTidslinjeTest.kt new file mode 100644 index 0000000000..4812f3ac4f --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/transformasjon/ZipTidslinjeTest.kt @@ -0,0 +1,48 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon + +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util.tilCharTidslinje +import no.nav.familie.tidslinje.utvidelser.tilPerioder +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import java.time.YearMonth + +class ZipTidslinjeTest { + @Test + fun testZipMedNesteTidslinje() { + val aTilF = ('a'..'f').toList().joinToString("") + val bokstavTidslinje = aTilF.tilCharTidslinje(YearMonth.now()) + val bokstavParTidslinje = bokstavTidslinje.zipMedNeste(ZipPadding.FØR) + + assertThat(aTilF).isEqualTo("abcdef") + + assertThat(bokstavParTidslinje.tilPerioder().map { it.verdi }).isEqualTo( + listOf(Pair(null, 'a'), Pair('a', 'b'), Pair('b', 'c'), Pair('c', 'd'), Pair('d', 'e'), Pair('e', 'f')), + ) + } + + @Test + fun testZipMedNesteTidslinjePaddingEtter() { + val aTilF = ('a'..'f').toList().joinToString("") + val bokstavTidslinje = aTilF.tilCharTidslinje(YearMonth.now()) + val bokstavParTidslinje = bokstavTidslinje.zipMedNeste(ZipPadding.ETTER) + + assertThat(aTilF).isEqualTo("abcdef") + + assertThat(bokstavParTidslinje.tilPerioder().map { it.verdi }).isEqualTo( + listOf(Pair('a', 'b'), Pair('b', 'c'), Pair('c', 'd'), Pair('d', 'e'), Pair('e', 'f'), Pair('f', null)), + ) + } + + @Test + fun testZipMedNesteTidslinjeIngenPadding() { + val aTilF = ('a'..'f').toList().joinToString("") + val bokstavTidslinje = aTilF.tilCharTidslinje(YearMonth.now()) + val bokstavParTidslinje = bokstavTidslinje.zipMedNeste(ZipPadding.INGEN_PADDING) + + assertThat(aTilF).isEqualTo("abcdef") + + assertThat(bokstavParTidslinje.tilPerioder().map { it.verdi }).isEqualTo( + listOf(Pair('a', 'b'), Pair('b', 'c'), Pair('c', 'd'), Pair('d', 'e'), Pair('e', 'f')), + ) + } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/BooleanCharTidslinje.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/BooleanCharTidslinje.kt new file mode 100644 index 0000000000..6d176c9bec --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/BooleanCharTidslinje.kt @@ -0,0 +1,16 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util + +import no.nav.familie.tidslinje.Tidslinje +import no.nav.familie.tidslinje.mapVerdi +import java.time.YearMonth + +fun String.somBoolskTidslinje(startTidspunkt: YearMonth) = this.tilCharTidslinje(startTidspunkt).somBoolsk() + +fun Tidslinje.somBoolsk() = + this.mapVerdi { + when (it?.lowercaseChar()) { + 't' -> true + 'f' -> false + else -> null + } + } diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/CharTidslinje.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/CharTidslinje.kt new file mode 100644 index 0000000000..7de58db41b --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/CharTidslinje.kt @@ -0,0 +1,41 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util + +import no.nav.familie.ba.sak.common.førsteDagIInneværendeMåned +import no.nav.familie.ba.sak.common.sisteDagIInneværendeMåned +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon.tilMåned +import no.nav.familie.tidslinje.Tidslinje +import no.nav.familie.tidslinje.TidslinjePeriodeMedDato +import no.nav.familie.tidslinje.tilTidslinje +import java.time.LocalDate +import java.time.YearMonth + +fun String.tilCharTidslinje(startTidspunkt: LocalDate): Tidslinje { + val erUendeligLengeSiden = firstOrNull() == '<' + val erUendeligLengeTil = lastOrNull() == '>' + val sisteIndeks = filter { it !in "<>" }.lastIndex + return this + .filter { it !in "<>" } + .mapIndexed { index, c -> + TidslinjePeriodeMedDato( + verdi = if (c == ' ') null else c, + fom = if (index == 0 && erUendeligLengeSiden) null else startTidspunkt.plusDays(index.toLong()), + tom = if (index == sisteIndeks && erUendeligLengeTil) null else startTidspunkt.plusDays(index.toLong()), + ) + }.tilTidslinje() +} + +fun String.tilCharTidslinje(startTidspunkt: YearMonth): Tidslinje { + val erUendeligLengeSiden = firstOrNull() == '<' + val erUendeligLengeTil = lastOrNull() == '>' + val sisteIndeks = filter { it !in "<>" }.lastIndex + return this + .filter { it !in "<>" } + .mapIndexed { index, c -> + TidslinjePeriodeMedDato( + verdi = if (c == ' ') null else c, + fom = if (index == 0 && erUendeligLengeSiden) null else startTidspunkt.plusMonths(index.toLong()).førsteDagIInneværendeMåned(), + tom = if (index == sisteIndeks && erUendeligLengeTil) null else startTidspunkt.plusMonths(index.toLong()).sisteDagIInneværendeMåned(), + ) + }.tilTidslinje() + .tilMåned { it.first() } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/CharTidslinjeTest.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/CharTidslinjeTest.kt new file mode 100644 index 0000000000..f8a6ab1118 --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/CharTidslinjeTest.kt @@ -0,0 +1,56 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util + +import no.nav.familie.ba.sak.common.rangeTo +import no.nav.familie.tidslinje.PRAKTISK_TIDLIGSTE_DAG +import no.nav.familie.tidslinje.utvidelser.tilPerioder +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Test + +class CharTidslinjeTest { + @Test + fun testEnkelCharTidsline() { + val tegn = "---------------" + val charTidslinje = tegn.tilCharTidslinje(jan(2020)) + + assertEquals(1.jan(2020), charTidslinje.startsTidspunkt) + assertEquals(tegn.length, charTidslinje.innhold.sumOf { it.lengde }) + + val periode = charTidslinje.tilPerioder().single() + + assertEquals(1.jan(2020), periode.fom) + assertEquals(31.mar(2021), periode.tom) + assertEquals('-', periode.verdi) + } + + @Test + fun testUendeligCharTidslinje() { + val tegn = "<--->" + val charTidslinje = tegn.tilCharTidslinje(jan(2020)) + + assertEquals(PRAKTISK_TIDLIGSTE_DAG, charTidslinje.startsTidspunkt) + + val periode = charTidslinje.tilPerioder().single() + + assertNull(periode.fom) + assertNull(periode.tom) + assertEquals('-', periode.verdi) + } + + @Test + fun testSammensattTidsline() { + val tegn = "aabbbbcdddddda" + val charTidslinje = tegn.tilCharTidslinje(jan(2020)) + + assertEquals(1.jan(2020), charTidslinje.startsTidspunkt) + assertEquals(tegn.length, charTidslinje.innhold.sumOf { it.lengde }) + + val perioder = charTidslinje.tilPerioder() + assertEquals(5, perioder.size) + assertEquals((jan(2020)..feb(2020)).med('a'), perioder[0]) + assertEquals((mar(2020)..jun(2020)).med('b'), perioder[1]) + assertEquals((jul(2020)..jul(2020)).med('c'), perioder[2]) + assertEquals((aug(2020)..jan(2021)).med('d'), perioder[3]) + assertEquals((feb(2021)..feb(2021)).med('a'), perioder[4]) + } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/StringTidslinje.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/StringTidslinje.kt new file mode 100644 index 0000000000..71b3331dc3 --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/StringTidslinje.kt @@ -0,0 +1,43 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util + +import no.nav.familie.ba.sak.common.førsteDagIInneværendeMåned +import no.nav.familie.ba.sak.common.sisteDagIInneværendeMåned +import no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.transformasjon.tilMåned +import no.nav.familie.tidslinje.Tidslinje +import no.nav.familie.tidslinje.TidslinjePeriodeMedDato +import no.nav.familie.tidslinje.tilTidslinje +import java.time.LocalDate +import java.time.YearMonth + +fun List.tilStringTidslinje(startTidspunkt: LocalDate): Tidslinje { + val erUendeligLengeSiden = firstOrNull() == "<" + val erUendeligLengeTil = lastOrNull() == ">" + val sisteIndeks = filter { it != "<" && it != ">" }.lastIndex + + return this + .filter { it != "<" && it != ">" } + .mapIndexed { index, s -> + TidslinjePeriodeMedDato( + verdi = s, + fom = if (index == 0 && erUendeligLengeSiden) null else startTidspunkt.plusDays(index.toLong()), + tom = if (index == sisteIndeks && erUendeligLengeTil) null else startTidspunkt.plusDays(index.toLong()), + ) + }.tilTidslinje() +} + +fun List.tilStringTidslinje(startTidspunkt: YearMonth): Tidslinje { + val erUendeligLengeSiden = firstOrNull() == "<" + val erUendeligLengeTil = lastOrNull() == ">" + val sisteIndeks = filter { it != "<" && it != ">" }.lastIndex + + return this + .filter { it != "<" && it != ">" } + .mapIndexed { index, s -> + TidslinjePeriodeMedDato( + verdi = s, + fom = if (index == 0 && erUendeligLengeSiden) null else startTidspunkt.plusMonths(index.toLong()).førsteDagIInneværendeMåned(), + tom = if (index == sisteIndeks && erUendeligLengeTil) null else startTidspunkt.plusMonths(index.toLong()).sisteDagIInneværendeMåned(), + ) + }.tilTidslinje() + .tilMåned { it.first() } +} diff --git a/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/Tid.kt b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/Tid.kt new file mode 100644 index 0000000000..276b86569b --- /dev/null +++ b/src/test/enhetstester/kotlin/no/nav/familie/ba/sak/kjerne/tidslinjefamiliefelles/util/Tid.kt @@ -0,0 +1,58 @@ +package no.nav.familie.ba.sak.kjerne.tidslinjefamiliefelles.util + +import no.nav.familie.ba.sak.common.YearMonthProgression +import no.nav.familie.ba.sak.common.førsteDagIInneværendeMåned +import no.nav.familie.ba.sak.common.sisteDagIInneværendeMåned +import no.nav.familie.tidslinje.Periode +import java.time.LocalDate +import java.time.YearMonth + +fun jan(år: Int) = YearMonth.of(år, 1) + +fun feb(år: Int) = YearMonth.of(år, 2) + +fun mar(år: Int) = YearMonth.of(år, 3) + +fun apr(år: Int) = YearMonth.of(år, 4) + +fun mai(år: Int) = YearMonth.of(år, 5) + +fun jun(år: Int) = YearMonth.of(år, 6) + +fun jul(år: Int) = YearMonth.of(år, 7) + +fun aug(år: Int) = YearMonth.of(år, 8) + +fun sep(år: Int) = YearMonth.of(år, 9) + +fun okt(år: Int) = YearMonth.of(år, 10) + +fun nov(år: Int) = YearMonth.of(år, 11) + +fun des(år: Int) = YearMonth.of(år, 12) + +fun Int.jan(år: Int) = LocalDate.of(år, 1, this) + +fun Int.feb(år: Int) = LocalDate.of(år, 2, this) + +fun Int.mar(år: Int) = LocalDate.of(år, 3, this) + +fun Int.apr(år: Int) = LocalDate.of(år, 4, this) + +fun Int.mai(år: Int) = LocalDate.of(år, 5, this) + +fun Int.jun(år: Int) = LocalDate.of(år, 6, this) + +fun Int.jul(år: Int) = LocalDate.of(år, 7, this) + +fun Int.aug(år: Int) = LocalDate.of(år, 8, this) + +fun Int.sep(år: Int) = LocalDate.of(år, 9, this) + +fun Int.okt(år: Int) = LocalDate.of(år, 10, this) + +fun Int.nov(år: Int) = LocalDate.of(år, 11, this) + +fun Int.des(år: Int) = LocalDate.of(år, 12, this) + +fun YearMonthProgression.med(verdi: V) = Periode(verdi, this.start.førsteDagIInneværendeMåned(), this.endInclusive.sisteDagIInneværendeMåned())