-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NAV-23269: Legger til tidslinje fra KS sak i familie-felles (#845)
* NAV-23269: Legger til tidslinje for BA og KS sak * NAV-23269: Legger til tidslinje for BA og KS sak * NAV-23269: Kjørte ktlint
- Loading branch information
Showing
29 changed files
with
4,020 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<parent> | ||
<groupId>no.nav.familie.felles</groupId> | ||
<artifactId>felles</artifactId> | ||
<version>${revision}${sha1}${changelist}</version> | ||
</parent> | ||
|
||
<artifactId>tidslinje</artifactId> | ||
<version>${revision}${sha1}${changelist}</version> | ||
<name>Felles - Tidslinje</name> | ||
<packaging>jar</packaging> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.jetbrains.kotlin</groupId> | ||
<artifactId>kotlin-stdlib</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.junit.jupiter</groupId> | ||
<artifactId>junit-jupiter-api</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
<build> | ||
<sourceDirectory>src/main/kotlin</sourceDirectory> | ||
<testSourceDirectory>src/test/kotlin</testSourceDirectory> | ||
|
||
<plugins> | ||
<plugin> | ||
<groupId>org.jetbrains.kotlin</groupId> | ||
<artifactId>kotlin-maven-plugin</artifactId> | ||
<version>${kotlin.version}</version> | ||
<executions> | ||
<execution> | ||
<id>compile</id> | ||
<phase>compile</phase> | ||
<goals> | ||
<goal>compile</goal> | ||
</goals> | ||
</execution> | ||
<execution> | ||
<id>test-compile</id> | ||
<phase>test-compile</phase> | ||
<goals> | ||
<goal>test-compile</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
</project> |
31 changes: 31 additions & 0 deletions
31
tidslinje/src/main/kotlin/no/nav/familie/tidslinje/Periode.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
@file:Suppress("UNCHECKED_CAST") | ||
|
||
package no.nav.familie.tidslinje | ||
|
||
import java.time.LocalDate | ||
|
||
data class Periode<T>( | ||
// NB: Generiske klasser arver type fra "Any?", så verdi kan være null. | ||
val verdi: T, | ||
val fom: LocalDate?, | ||
val tom: LocalDate?, | ||
) { | ||
fun tilTidslinjePeriodeMedDato() = TidslinjePeriodeMedDato(verdi, fom, tom) | ||
} | ||
|
||
fun <T> List<Periode<T>>.tilTidslinje(): Tidslinje<T> = | ||
this | ||
.map { it.tilTidslinjePeriodeMedDato() } | ||
.sortedBy { it.fom } | ||
.tilTidslinje() | ||
|
||
fun <T> List<Periode<T>>.filtrerIkkeNull(): List<Periode<T & Any>> = | ||
this.mapNotNull { periode -> periode.verdi?.let { periode as Periode<T & Any> } } | ||
|
||
fun <T> List<Periode<T>>.verdier(): List<T> = this.map { it.verdi } | ||
|
||
data class IkkeNullbarPeriode<T>( | ||
val verdi: T, | ||
val fom: LocalDate, | ||
val tom: LocalDate, | ||
) |
207 changes: 207 additions & 0 deletions
207
tidslinje/src/main/kotlin/no/nav/familie/tidslinje/Tidslinje.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
package no.nav.familie.tidslinje | ||
|
||
import no.nav.familie.tidslinje.utvidelser.klipp | ||
import no.nav.familie.tidslinje.utvidelser.kombinerMed | ||
import no.nav.familie.tidslinje.utvidelser.map | ||
import no.nav.familie.tidslinje.utvidelser.mapper | ||
import no.nav.familie.tidslinje.utvidelser.tilPerioder | ||
import java.time.DayOfWeek | ||
import java.time.LocalDate | ||
import java.time.temporal.TemporalAdjusters | ||
|
||
enum class TidsEnhet { | ||
DAG, | ||
UKE, | ||
MÅNED, | ||
ÅR, | ||
} | ||
|
||
/** | ||
* En tidslinje består av ulike verdier over tid. Det vil si at en tidslinje kan ha en verdi | ||
* A over et tidsintervall og en annen verdi B over et senere tidsintervall. Disse tidsintervallene blir håndert av Periode-klassen. | ||
* En tidslinje kan visualiseres som en liste [A, A, A, A, ... , A, B, B, ... , B, C, C, ... , C] og et startstidspunkt, | ||
* hvor A, B og C er ulike verdier og hver plass i lista representerer en dag. | ||
* Tidslinjer kan innholde uendelige perioder. Det kan ikke være flere perioder med andre verdier etter en uendelig periode. | ||
* En tidslinje støtter verdier av typen [Udefinert], [Null] og [PeriodeVerdi]. En verdi er udefinert når vi ikke vet | ||
* hva verdien skal være (et hull i tidslinja). En verdi er no.nav.familie.tidslinje.Null når vi vet at det ikke finnes noe verdi i dette tidsrommet. | ||
*/ | ||
open class Tidslinje<T>( | ||
var startsTidspunkt: LocalDate, | ||
perioder: List<TidslinjePeriode<T>>, | ||
var tidsEnhet: TidsEnhet = TidsEnhet.DAG, | ||
) { | ||
var innhold: List<TidslinjePeriode<T>> = emptyList() | ||
set(verdi) { | ||
field = this.lagInnholdBasertPåPeriodelengder(verdi) | ||
} | ||
val foreldre: MutableList<Tidslinje<Any>> = mutableListOf() | ||
var tittel = "" | ||
|
||
init { | ||
this.innhold = perioder | ||
startsTidspunkt = | ||
when (tidsEnhet) { | ||
TidsEnhet.ÅR -> startsTidspunkt.withDayOfYear(1) | ||
TidsEnhet.MÅNED -> startsTidspunkt.withDayOfMonth(1) | ||
TidsEnhet.UKE -> startsTidspunkt.with(DayOfWeek.MONDAY) | ||
TidsEnhet.DAG -> startsTidspunkt | ||
} | ||
} | ||
|
||
fun erTom() = innhold.sumOf { it.lengde } == 0 | ||
|
||
/** | ||
* Kalkulerer slutttidspunkt som en LocalDate. | ||
* Funksjonen returnerer den siste dagen som er med i tidslinja | ||
* Om tidslinja er uendelig, kastes det et unntak | ||
*/ | ||
fun kalkulerSluttTidspunkt(): LocalDate { | ||
val antallTidsEnheter: Int = this.innhold.sumOf { it.lengde } | ||
val sluttTidspunkt = this.startsTidspunkt.plus(antallTidsEnheter.toLong() - 1, mapper[this.tidsEnhet]) | ||
|
||
return when (this.tidsEnhet) { | ||
TidsEnhet.ÅR -> sluttTidspunkt.with(TemporalAdjusters.lastDayOfYear()) | ||
TidsEnhet.MÅNED -> sluttTidspunkt.with(TemporalAdjusters.lastDayOfMonth()) | ||
TidsEnhet.UKE -> sluttTidspunkt.with(DayOfWeek.SUNDAY) | ||
TidsEnhet.DAG -> sluttTidspunkt | ||
} | ||
} | ||
|
||
private fun kalkulerSluttTidspunkt(sluttDato: LocalDate): LocalDate = | ||
when (this.tidsEnhet) { | ||
TidsEnhet.ÅR -> sluttDato.with(TemporalAdjusters.lastDayOfYear()) | ||
TidsEnhet.MÅNED -> sluttDato.with(TemporalAdjusters.lastDayOfMonth()) | ||
TidsEnhet.UKE -> sluttDato.with(DayOfWeek.SUNDAY) | ||
TidsEnhet.DAG -> sluttDato | ||
} | ||
|
||
override fun toString(): String = | ||
"StartTidspunkt: " + startsTidspunkt + " Tidsenhet: " + tidsEnhet + | ||
" Total lengde: " + innhold.sumOf { it.lengde } + | ||
" Perioder: " + | ||
innhold.mapIndexed { indeks, it -> | ||
"(Verdi: " + it.periodeVerdi.verdi.toString() + | ||
", fom: " + | ||
startsTidspunkt.plus( | ||
innhold.take(indeks).sumOf { it.lengde }.toLong(), | ||
mapper[this.tidsEnhet], | ||
) + | ||
", tom:" + | ||
kalkulerSluttTidspunkt( | ||
startsTidspunkt.plus( | ||
innhold.take(indeks).sumOf { it.lengde }.toLong() + it.lengde - 1, | ||
mapper[this.tidsEnhet], | ||
), | ||
) + | ||
")" | ||
} | ||
|
||
private fun lagInnholdBasertPåPeriodelengder(innhold: List<TidslinjePeriode<T>>): List<TidslinjePeriode<T>> { | ||
val arr = mutableListOf<TidslinjePeriode<T>>() | ||
|
||
var i = 0 | ||
while (i < innhold.size) { | ||
var j = i + 1 | ||
var lengde = innhold[i].lengde | ||
|
||
while (j < innhold.size && innhold[i].periodeVerdi == innhold[j].periodeVerdi) { | ||
lengde += innhold[j].lengde | ||
j++ | ||
if (j >= innhold.size) break | ||
} | ||
|
||
val tidslinjePeriode = | ||
TidslinjePeriode( | ||
periodeVerdi = innhold[i].periodeVerdi, | ||
lengde = lengde, | ||
erUendelig = false, | ||
) | ||
i = j | ||
arr.add(tidslinjePeriode) | ||
if (tidslinjePeriode.erUendelig) return arr.toList() | ||
} | ||
|
||
return arr.toList() | ||
} | ||
|
||
override fun equals(other: Any?): Boolean { | ||
if (other !is Tidslinje<*>) return false | ||
return other.startsTidspunkt == this.startsTidspunkt && | ||
other.innhold == this.innhold && | ||
other.tidsEnhet == this.tidsEnhet && | ||
other.tittel == this.tittel | ||
} | ||
|
||
override fun hashCode(): Int = | ||
this.startsTidspunkt.hashCode() + this.innhold.hashCode() + this.tidsEnhet.hashCode() + this.tittel.hashCode() | ||
|
||
companion object | ||
} | ||
|
||
fun <T> tomTidslinje( | ||
startsTidspunkt: LocalDate? = null, | ||
tidsEnhet: TidsEnhet = TidsEnhet.DAG, | ||
): Tidslinje<T> = Tidslinje(startsTidspunkt = startsTidspunkt ?: PRAKTISK_TIDLIGSTE_DAG, emptyList(), tidsEnhet) | ||
|
||
fun <K, V, H, R> Map<K, Tidslinje<V>>.leftJoin( | ||
høyreTidslinjer: Map<K, Tidslinje<H>>, | ||
kombinator: (V?, H?) -> R?, | ||
): Map<K, Tidslinje<R>> { | ||
val venstreTidslinjer = this | ||
val venstreNøkler = venstreTidslinjer.keys | ||
|
||
return venstreNø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) | ||
} | ||
} | ||
|
||
fun <K, V, H, R> Map<K, Tidslinje<V>>.outerJoin( | ||
høyreTidslinjer: Map<K, Tidslinje<H>>, | ||
kombinator: (V?, H?) -> R?, | ||
): Map<K, Tidslinje<R>> { | ||
val venstreTidslinjer = this | ||
val alleNøkler = venstreTidslinjer.keys + 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) | ||
} | ||
} | ||
|
||
fun <K, A, B, C, R> Map<K, Tidslinje<A>>.outerJoin( | ||
tidslinjer2: Map<K, Tidslinje<B>>, | ||
tidslinjer3: Map<K, Tidslinje<C>>, | ||
kombinator: (A?, B?, C?) -> R?, | ||
): Map<K, Tidslinje<R>> { | ||
val tidslinjer1 = this | ||
val alleNøkler = tidslinjer1.keys + tidslinjer2.keys + tidslinjer3.keys | ||
|
||
return alleNøkler.associateWith { nøkkel -> | ||
val tidslinje1 = tidslinjer1.getOrDefault(nøkkel, tomTidslinje()) | ||
val tidslinje2 = tidslinjer2.getOrDefault(nøkkel, tomTidslinje()) | ||
val tidslinje3 = tidslinjer3.getOrDefault(nøkkel, tomTidslinje()) | ||
|
||
tidslinje1.kombinerMed(tidslinje2, tidslinje3, kombinator) | ||
} | ||
} | ||
|
||
fun <T> Tidslinje<T>.beskjærEtter(tidslinje: Tidslinje<*>): Tidslinje<T> = | ||
this.klipp(tidslinje.startsTidspunkt, tidslinje.kalkulerSluttTidspunkt()) | ||
|
||
fun <T> Tidslinje<T>.inneholder(verdi: T): Boolean = this.tilPerioder().any { it.verdi == verdi } | ||
|
||
fun <T, R> Tidslinje<T>.mapVerdi(mapper: (T?) -> R): Tidslinje<R> = | ||
this.map { periodeVerdi -> | ||
when (periodeVerdi) { | ||
is Verdi, | ||
is Null, | ||
-> mapper(periodeVerdi.verdi)?.let { Verdi(it) } ?: Null() | ||
|
||
is Udefinert -> Udefinert() | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
tidslinje/src/main/kotlin/no/nav/familie/tidslinje/TidslinjePeriode.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package no.nav.familie.tidslinje | ||
|
||
const val INF = 1_000_000_000 | ||
|
||
sealed class PeriodeVerdi<T>( | ||
protected val _verdi: T?, | ||
) { | ||
override operator fun equals(other: Any?): Boolean { | ||
if (other !is PeriodeVerdi<*>) return false | ||
if (other._verdi == this._verdi) return true | ||
return false | ||
} | ||
|
||
override fun hashCode(): Int = this._verdi.hashCode() | ||
|
||
abstract val verdi: T? | ||
} | ||
|
||
class Verdi<T>( | ||
override val verdi: T & Any, | ||
) : PeriodeVerdi<T>(verdi) | ||
|
||
class Udefinert<T> : PeriodeVerdi<T>(null) { | ||
override fun equals(other: Any?): Boolean = other is Udefinert<*> | ||
|
||
override fun hashCode(): Int = this._verdi.hashCode() | ||
|
||
override val verdi: T? = this._verdi | ||
} | ||
|
||
class Null<T> : PeriodeVerdi<T>(null) { | ||
override fun equals(other: Any?): Boolean = other is Null<*> | ||
|
||
override fun hashCode(): Int = this._verdi.hashCode() | ||
|
||
override val verdi: T? = this._verdi | ||
} | ||
|
||
/** | ||
* En periode representerer et tidsintervall hvor en tidslinje har en konstant verdi. | ||
* En periode varer en tid [lengde], og kan være uendelig. | ||
* Om [lengde] > [INF] eller [erUendelig] er satt til true, behandles perioden som at den har uendelig lengde. | ||
* En tidslinje støtter verdier av typen [Udefinert], [Null] og [PeriodeVerdi]. En verdi er udefinert når vi ikke vet | ||
* hva verdien skal være (et hull i tidslinja). En verdi er no.nav.familie.tidslinje.Null når vi vet at det ikke finnes en verdi i dette tidsrommet. | ||
*/ | ||
data class TidslinjePeriode<T>( | ||
val periodeVerdi: PeriodeVerdi<T>, | ||
var lengde: Int, | ||
var erUendelig: Boolean = false, | ||
) { | ||
init { | ||
if (lengde >= INF) { | ||
erUendelig = true | ||
} | ||
if (erUendelig && lengde < INF) { | ||
lengde = INF | ||
} | ||
if (lengde <= 0) { | ||
throw java.lang.IllegalArgumentException("lengde må være større enn null.") | ||
} | ||
} | ||
|
||
constructor(periodeVerdi: T?, lengde: Int, erUendelig: Boolean = false) : this( | ||
if (periodeVerdi == null) { | ||
Null() | ||
} else { | ||
Verdi( | ||
periodeVerdi, | ||
) | ||
}, | ||
lengde, | ||
erUendelig, | ||
) | ||
|
||
override fun toString(): String = "Verdi: " + periodeVerdi.verdi.toString() + ", Lengde: " + lengde | ||
} | ||
|
||
fun <T> T?.tilPeriodeVerdi(): PeriodeVerdi<T> = this?.let { Verdi(it) } ?: Null() |
Oops, something went wrong.