From 15bf42da20a2c36e1645f886deec4f8b06b2f62c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20Wikstr=C3=B6m?= Date: Mon, 23 Dec 2024 16:01:56 +0200 Subject: [PATCH 1/3] Palauta 100 oppijaa per tiedosto --- ...ritusrekisteriMuuttuneetJalkeenQuery.scala | 16 ++++++---- .../SuoritusrekisteriOppijaOidsQuery.scala | 2 +- .../SuoritusrekisteriQuery.scala | 30 ++++++++++++------- .../SuoritusrekisteriResponse.scala | 5 ++-- .../massaluovutus/MassaluovutusSpec.scala | 24 ++++++++------- 5 files changed, 47 insertions(+), 30 deletions(-) diff --git a/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriMuuttuneetJalkeenQuery.scala b/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriMuuttuneetJalkeenQuery.scala index 4e7a97a830..a8535c8e98 100644 --- a/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriMuuttuneetJalkeenQuery.scala +++ b/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriMuuttuneetJalkeenQuery.scala @@ -26,11 +26,17 @@ case class SuoritusrekisteriMuuttuneetJalkeenQuery( QueryMethods.runDbSync( db, sql""" - SELECT opiskeluoikeus.id, opiskeluoikeus.aikaleima, coalesce(henkilo.master_oid, henkilo.oid) + SELECT opiskeluoikeus.id, + opiskeluoikeus.aikaleima, + COALESCE(henkilo.master_oid, henkilo.oid) AS master_oid FROM opiskeluoikeus - JOIN henkilo ON henkilo.oid = opiskeluoikeus.oppija_oid - WHERE aikaleima >= ${Timestamp.valueOf(muuttuneetJälkeen)} - AND koulutusmuoto = any(${SuoritusrekisteriQuery.opiskeluoikeudenTyypit}) - ORDER BY aikaleima + JOIN henkilo ON henkilo.oid = opiskeluoikeus.oppija_oid + WHERE COALESCE(henkilo.master_oid, henkilo.oid) IN ( + SELECT DISTINCT COALESCE(h.master_oid, h.oid) AS master_oid + FROM henkilo h + JOIN opiskeluoikeus o ON h.oid = o.oppija_oid + WHERE aikaleima >= ${Timestamp.valueOf(muuttuneetJälkeen)} + AND o.koulutusmuoto = ANY(${SuoritusrekisteriQuery.opiskeluoikeudenTyypit})) + ORDER BY opiskeluoikeus.aikaleima """.as[(Int, Timestamp, String)]) } diff --git a/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriOppijaOidsQuery.scala b/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriOppijaOidsQuery.scala index 8a3afaf3e3..62356f443d 100644 --- a/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriOppijaOidsQuery.scala +++ b/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriOppijaOidsQuery.scala @@ -24,7 +24,7 @@ case class SuoritusrekisteriOppijaOidsQuery( QueryMethods.runDbSync( db, sql""" - SELECT opiskeluoikeus.id, opiskeluoikeus.aikaleima, coalesce(henkilo.master_oid, henkilo.oid) as oid + SELECT opiskeluoikeus.id, opiskeluoikeus.aikaleima, coalesce(henkilo.master_oid, henkilo.oid) as master_oid FROM opiskeluoikeus JOIN henkilo ON henkilo.oid = opiskeluoikeus.oppija_oid WHERE diff --git a/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriQuery.scala b/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriQuery.scala index 4126a86e5d..2a2fd4f703 100644 --- a/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriQuery.scala +++ b/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriQuery.scala @@ -7,6 +7,7 @@ import fi.oph.koski.db.{DB, KoskiOpiskeluoikeusRow, KoskiTables, QueryMethods} import fi.oph.koski.koskiuser.KoskiSpecificSession import fi.oph.koski.koskiuser.Rooli.{OPHKATSELIJA, OPHPAAKAYTTAJA} import fi.oph.koski.log._ +import fi.oph.koski.massaluovutus.suoritusrekisteri.opiskeluoikeus.SureOpiskeluoikeus import fi.oph.koski.massaluovutus.{MassaluovutusQueryParameters, MassaluovutusQueryPriority, QueryResultWriter} import fi.oph.koski.schema.{KoskeenTallennettavaOpiskeluoikeus, KoskiSchema} @@ -21,18 +22,25 @@ trait SuoritusrekisteriQuery extends MassaluovutusQueryParameters with Logging { override def run(application: KoskiApplication, writer: QueryResultWriter)(implicit user: KoskiSpecificSession): Either[String, Unit] = { val opiskeluoikeudetResult = getOpiskeluoikeusIds(application.masterDatabase.db) - val oppijaOidit = opiskeluoikeudetResult.groupBy(_._3) + val resultsByOppija = opiskeluoikeudetResult.groupBy(_._3) - writer.predictFileCount(oppijaOidit.size) - oppijaOidit.grouped(100).foreach { groupedResult => - val db = selectDbByLag(application, groupedResult.head._2.head._2) - groupedResult.foreach { case (oppija_oid, opiskeluoikeudet) => + writer.predictFileCount(resultsByOppija.size / 100) + resultsByOppija.grouped(100).zipWithIndex.foreach { case (oppijaResult, index) => + val sureResponses = oppijaResult.map { case (oppija_oid, opiskeluoikeudet) => + val latestTimestamp = opiskeluoikeudet.maxBy(_._2.toInstant)._2 + val db = selectDbByLag(application, latestTimestamp) val response = opiskeluoikeudet.flatMap(oo => getOpiskeluoikeus(application, db, oo._1)) response.foreach { oo => - auditLog(oo.oppijaOid, oo.opiskeluoikeus.oid) + auditLog(oppija_oid, oo.oid) } - writer.putJson(s"$oppija_oid", response) + SureResponse( + oppijaOid = oppija_oid, + kaikkiOidit = application.henkilöRepository.findByOid(oppija_oid).get.kaikkiOidit, + aikaleima = LocalDateTime.from(latestTimestamp.toLocalDateTime), + opiskeluoikeudet = response + ) } + writer.putJson(s"$index", sureResponses) } Right(()) } @@ -40,7 +48,7 @@ trait SuoritusrekisteriQuery extends MassaluovutusQueryParameters with Logging { override def queryAllowed(application: KoskiApplication)(implicit user: KoskiSpecificSession): Boolean = user.hasRole(OPHKATSELIJA) || user.hasRole(OPHPAAKAYTTAJA) - private def getOpiskeluoikeus(application: KoskiApplication, db: DB, id: Int): Option[SureResponse] = + private def getOpiskeluoikeus(application: KoskiApplication, db: DB, id: Int): Option[SureOpiskeluoikeus] = QueryMethods.runDbSync( db, sql""" @@ -48,7 +56,7 @@ trait SuoritusrekisteriQuery extends MassaluovutusQueryParameters with Logging { FROM opiskeluoikeus WHERE id = $id """.as[KoskiOpiskeluoikeusRow] - ).headOption.flatMap(toResponse(application)) + ).headOption.flatMap(toSureOpiskeluoikeus(application)) private def selectDbByLag(application: KoskiApplication, opiskeluoikeusAikaleima: Timestamp): DB = { val safetyLimit = 15.seconds @@ -62,11 +70,11 @@ trait SuoritusrekisteriQuery extends MassaluovutusQueryParameters with Logging { } } - private def toResponse(application: KoskiApplication)(row: KoskiOpiskeluoikeusRow): Option[SureResponse] = { + private def toSureOpiskeluoikeus(application: KoskiApplication)(row: KoskiOpiskeluoikeusRow): Option[SureOpiskeluoikeus] = { val json = KoskiTables.KoskiOpiskeluoikeusTable.readAsJValue(row.data, row.oid, row.versionumero, row.aikaleima) application.validatingAndResolvingExtractor.extract[KoskeenTallennettavaOpiskeluoikeus](KoskiSchema.strictDeserialization)(json) match { case Right(oo: KoskeenTallennettavaOpiskeluoikeus) => - SureOpiskeluoikeus(oo).map(SureResponse(row.oppijaOid, row.aikaleima.toLocalDateTime, _)) + SureOpiskeluoikeusO(oo) case Left(errors) => logger.warn(s"Error deserializing opiskeluoikeus: ${errors}") None diff --git a/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriResponse.scala b/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriResponse.scala index 5e95572a74..c887d1e01e 100644 --- a/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriResponse.scala +++ b/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriResponse.scala @@ -9,8 +9,9 @@ import java.time.LocalDateTime case class SureResponse( oppijaOid: String, + kaikkiOidit: Seq[String], aikaleima: LocalDateTime, - opiskeluoikeus: SureOpiskeluoikeus, + opiskeluoikeudet: Seq[SureOpiskeluoikeus], ) object SureResponse { @@ -18,7 +19,7 @@ object SureResponse { SchemaToJson.toJsonSchema(KoskiSchema.createSchema(classOf[SureResponse]).asInstanceOf[ClassSchema]) } -object SureOpiskeluoikeus { +object SureOpiskeluoikeusO { def apply(oo: KoskeenTallennettavaOpiskeluoikeus): Option[SureOpiskeluoikeus] = (oo match { case o: PerusopetuksenOpiskeluoikeus => Some(SurePerusopetuksenOpiskeluoikeus(o)) diff --git a/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala b/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala index 69ac64167e..1f595f909a 100644 --- a/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala +++ b/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala @@ -22,6 +22,7 @@ import org.json4s.{JArray, JInt, JNothing, JObject, JValue} import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach} +import shapeless.syntax.std.tuple.productTupleOps import java.net.URL import java.sql.Timestamp @@ -506,9 +507,8 @@ class MassaluovutusSpec extends AnyFreeSpec with KoskiHttpSpec with Matchers wit oos.arr.map(v => ( (v \ "oppijaOid").extract[String], - (v \ "opiskeluoikeus" \ "oid").extract[String], - )).foreach { case (oppijaOid, opiskeluoikeusOid) => - AuditLogTester.verifyLastAuditLogMessage(Map( + ((v \ "opiskeluoikeudet").extract[List[JObject]].last \ "oid").extract[String])).last match { + case (oppijaOid, opiskeluoikeusOid) => AuditLogTester.verifyLastAuditLogMessage(Map( "operation" -> "SUORITUSREKISTERI_OPISKELUOIKEUS_HAKU", "target" -> Map( "oppijaHenkiloOid" -> oppijaOid, @@ -517,8 +517,6 @@ class MassaluovutusSpec extends AnyFreeSpec with KoskiHttpSpec with Matchers wit )) } } - - } } @@ -540,7 +538,10 @@ class MassaluovutusSpec extends AnyFreeSpec with KoskiHttpSpec with Matchers wit val oos = jsonFiles.flatMap { case JArray(a) => a case _ => Nil - }.map(_ \ "opiskeluoikeus") + }.map(_ \ "opiskeluoikeudet").flatMap { + case JArray(a) => a + case _ => Nil + } tyyppi match { case Some(t) => oos.filter(oo => (oo \ "tyyppi" \ "koodiarvo").extract[String] == t) case None => oos @@ -679,9 +680,8 @@ class MassaluovutusSpec extends AnyFreeSpec with KoskiHttpSpec with Matchers wit oos.arr.map(v => ( (v \ "oppijaOid").extract[String], - (v \ "opiskeluoikeus" \ "oid").extract[String], - )).foreach { case (oppijaOid, opiskeluoikeusOid) => - AuditLogTester.verifyLastAuditLogMessage(Map( + ((v \ "opiskeluoikeudet").extract[List[JObject]].last \ "oid").extract[String])).last match { + case (oppijaOid, opiskeluoikeusOid) => AuditLogTester.verifyLastAuditLogMessage(Map( "operation" -> "SUORITUSREKISTERI_OPISKELUOIKEUS_HAKU", "target" -> Map( "oppijaHenkiloOid" -> oppijaOid, @@ -690,7 +690,6 @@ class MassaluovutusSpec extends AnyFreeSpec with KoskiHttpSpec with Matchers wit )) } } - } } @@ -712,7 +711,10 @@ class MassaluovutusSpec extends AnyFreeSpec with KoskiHttpSpec with Matchers wit val oos = jsonFiles.flatMap { case JArray(a) => a case _ => Nil - }.map(_ \ "opiskeluoikeus") + }.map(_ \ "opiskeluoikeudet").flatMap { + case JArray(a) => a + case _ => Nil + } tyyppi match { case Some(t) => oos.filter(oo => (oo \ "tyyppi" \ "koodiarvo").extract[String] == t) case None => oos From 7e80dbabd4e3fd21023bb73ccbec73c114c8cf08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20Wikstr=C3=B6m?= Date: Mon, 23 Dec 2024 18:56:44 +0200 Subject: [PATCH 2/3] OppijaOids haku master oideilla --- .../SuoritusrekisteriOppijaOidsQuery.scala | 12 +++-- .../massaluovutus/MassaluovutusSpec.scala | 51 +++++++++++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriOppijaOidsQuery.scala b/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriOppijaOidsQuery.scala index 62356f443d..ba1cd026ea 100644 --- a/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriOppijaOidsQuery.scala +++ b/src/main/scala/fi/oph/koski/massaluovutus/suoritusrekisteri/SuoritusrekisteriOppijaOidsQuery.scala @@ -26,11 +26,13 @@ case class SuoritusrekisteriOppijaOidsQuery( sql""" SELECT opiskeluoikeus.id, opiskeluoikeus.aikaleima, coalesce(henkilo.master_oid, henkilo.oid) as master_oid FROM opiskeluoikeus - JOIN henkilo ON henkilo.oid = opiskeluoikeus.oppija_oid - WHERE - (henkilo.oid = any($oppijaOids) OR - henkilo.master_oid = any($oppijaOids)) - AND koulutusmuoto = any(${SuoritusrekisteriQuery.opiskeluoikeudenTyypit}) + JOIN henkilo ON henkilo.oid = opiskeluoikeus.oppija_oid + WHERE COALESCE(henkilo.master_oid, henkilo.oid) IN ( + SELECT DISTINCT COALESCE(h.master_oid, h.oid) + FROM henkilo h + WHERE h.oid = any($oppijaOids) + ) + AND opiskeluoikeus.koulutusmuoto = any(${SuoritusrekisteriQuery.opiskeluoikeudenTyypit}) """.as[(Int, Timestamp, String)]) } } diff --git a/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala b/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala index 1f595f909a..5d9efb1022 100644 --- a/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala +++ b/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala @@ -810,6 +810,57 @@ class MassaluovutusSpec extends AnyFreeSpec with KoskiHttpSpec with Matchers wit )) } } + + "Haku slave oidilla palauttaa master-oppijan" in { + val query = getQuery(Seq(KoskiSpecificMockOppijat.slave.henkilö.oid)) + val queryId = addQuerySuccessfully(query, user) { response => + response.status should equal(QueryState.pending) + response.queryId + } + val complete = waitForCompletion(queryId, user) + + val jsonFiles = complete.files.map { file => + verifyResultAndContent(file, user) { + JsonMethods.parse(response.body) + } + } + + (jsonFiles.head.extract[Seq[JObject]].head \ "oppijaOid").extract[String] should equal(KoskiSpecificMockOppijat.master.oid) + } + + "Haku master oidilla palauttaa oppijan slave-oidineen" in { + val query = getQuery(Seq(KoskiSpecificMockOppijat.master.oid)) + val queryId = addQuerySuccessfully(query, user) { response => + response.status should equal(QueryState.pending) + response.queryId + } + val complete = waitForCompletion(queryId, user) + + val jsonFiles = complete.files.map { file => + verifyResultAndContent(file, user) { + JsonMethods.parse(response.body) + } + } + + (jsonFiles.head.extract[Seq[JObject]].head \ "kaikkiOidit").extract[Set[String]] should equal(Set(KoskiSpecificMockOppijat.master.oid, KoskiSpecificMockOppijat.slave.henkilö.oid)) + } + + "Haku master- ja slave oidilla samaan aikaan palauttaa vain yhden oppijan" in { + val query = getQuery(Seq(KoskiSpecificMockOppijat.master.oid, KoskiSpecificMockOppijat.slave.henkilö.oid)) + val queryId = addQuerySuccessfully(query, user) { response => + response.status should equal(QueryState.pending) + response.queryId + } + val complete = waitForCompletion(queryId, user) + + val jsonFiles = complete.files.map { file => + verifyResultAndContent(file, user) { + JsonMethods.parse(response.body) + } + } + + jsonFiles.head.extract[Seq[JObject]].length should equal(1) + } } def addQuery[T](query: MassaluovutusQueryParameters, user: UserWithPassword)(f: => T): T = From 7c517528e7ff106b8729844f986f372b2d9b8e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20Wikstr=C3=B6m?= Date: Fri, 27 Dec 2024 13:05:51 +0200 Subject: [PATCH 3/3] =?UTF-8?q?Lis=C3=A4=C3=A4=20testi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../massaluovutus/MassaluovutusSpec.scala | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala b/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala index 5d9efb1022..9ddf4d5a8c 100644 --- a/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala +++ b/src/test/scala/fi/oph/koski/massaluovutus/MassaluovutusSpec.scala @@ -1,5 +1,6 @@ package fi.oph.koski.massaluovutus +import fi.oph.koski.api.misc.OpiskeluoikeusTestMethodsAmmatillinen import fi.oph.koski.db.PostgresDriverWithJsonSupport.api.actionBasedSQLInterpolation import fi.oph.koski.db.QueryMethods import fi.oph.koski.henkilo.KoskiSpecificMockOppijat @@ -14,6 +15,7 @@ import fi.oph.koski.massaluovutus.valintalaskenta.ValintalaskentaQuery import fi.oph.koski.organisaatio.MockOrganisaatiot import fi.oph.koski.raportit.RaportitService import fi.oph.koski.schema.KoskiSchema.strictDeserialization +import fi.oph.koski.schema.{KoskeenTallennettavaOpiskeluoikeus, LocalizedString, PerusopetuksenVuosiluokanSuoritus} import fi.oph.koski.util.Wait import fi.oph.koski.{KoskiApplicationForTests, KoskiHttpSpec} import fi.oph.scalaschema.Serializer.format @@ -22,15 +24,13 @@ import org.json4s.{JArray, JInt, JNothing, JObject, JValue} import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach} -import shapeless.syntax.std.tuple.productTupleOps import java.net.URL import java.sql.Timestamp import java.time.{Duration, LocalDate, LocalDateTime} import java.util.UUID -import scala.util.matching.Regex -class MassaluovutusSpec extends AnyFreeSpec with KoskiHttpSpec with Matchers with BeforeAndAfterAll with BeforeAndAfterEach { +class MassaluovutusSpec extends AnyFreeSpec with KoskiHttpSpec with Matchers with BeforeAndAfterAll with BeforeAndAfterEach with OpiskeluoikeusTestMethodsAmmatillinen { val app = KoskiApplicationForTests override protected def beforeAll(): Unit = { @@ -635,6 +635,33 @@ class MassaluovutusSpec extends AnyFreeSpec with KoskiHttpSpec with Matchers wit )) } } + + "Palauttaa oppijan kaikki opiskeluoikeudet jos yksikin on muuttunut" in { + val oppija = KoskiSpecificMockOppijat.moniaEriOpiskeluoikeuksia + val oo = getOpiskeluoikeus(oppija.oid, "perusopetus") + val muokattuOo = oo.withSuoritukset(oo.suoritukset.map { + case p: PerusopetuksenVuosiluokanSuoritus => p.copy(todistuksellaNäkyvätLisätiedot = Some(LocalizedString.finnish("asd"))) + case s => s + }) + createOrUpdate(oppija, muokattuOo) + val tallennettuOo = getOpiskeluoikeus(muokattuOo.oid.get).asInstanceOf[KoskeenTallennettavaOpiskeluoikeus] + + val query = getQuery(tallennettuOo.aikaleima.get) + val queryId = addQuerySuccessfully(query, user) { response => + response.status should equal(QueryState.pending) + response.queryId + } + val complete = waitForCompletion(queryId, user) + + val jsonFiles = complete.files.map { file => + verifyResultAndContent(file, user) { + JsonMethods.parse(response.body) + } + } + val oppijat = jsonFiles.head.extract[Seq[JObject]] + oppijat.length should equal(1) + (oppijat.head \ "opiskeluoikeudet").extract[Seq[JObject]].length should equal(7) + } } "Suoritusrekisterikysely - oppija-oideilla hakeminen" - {