From 6db1a6ac43cc59f45302ae9c017709fcfe893e6e Mon Sep 17 00:00:00 2001 From: Tatu Repo Date: Mon, 3 Feb 2025 16:07:25 +0200 Subject: [PATCH] Fixed assistance level counting and changed stat day for yearly values --- .../reports/TampereRegionalSurvey.tsx | 35 ++- .../lib-common/generated/api-types/reports.ts | 9 +- .../defaults/employee/i18n/fi.tsx | 21 +- .../reports/TampereRegionalSurveyTest.kt | 261 ++++++++++++++++++ .../evaka/reports/TampereRegionalSurvey.kt | 85 +++--- 5 files changed, 356 insertions(+), 55 deletions(-) diff --git a/frontend/src/employee-frontend/components/reports/TampereRegionalSurvey.tsx b/frontend/src/employee-frontend/components/reports/TampereRegionalSurvey.tsx index 3ef8d867b66..8c922c98c69 100644 --- a/frontend/src/employee-frontend/components/reports/TampereRegionalSurvey.tsx +++ b/frontend/src/employee-frontend/components/reports/TampereRegionalSurvey.tsx @@ -51,7 +51,7 @@ interface ReportSelection { const emptyReportSelection = { monthlyStatistics: false, ageStatistics: false, - yearlyStatistics: false, + yearlyStatistics: false } const emptyMonthlyValue: RegionalSurveyReportResult = { @@ -345,7 +345,8 @@ export default React.memo(function TampereRegionalSurveyReport() { key: 'club5YearOldCount' }, { - label: t.yearlyStatisticsColumns.preschoolDaycareUnitCareCount, + label: + t.yearlyStatisticsColumns.preschoolDaycareUnitCareCount, key: 'preschoolDaycareUnitCareCount' }, { @@ -353,20 +354,38 @@ export default React.memo(function TampereRegionalSurveyReport() { key: 'preschoolDaycareFamilyCareCount' }, { - label: t.yearlyStatisticsColumns.generalAssistanceCount, - key: 'generalAssistanceCount' + label: + t.yearlyStatisticsColumns.voucherGeneralAssistanceCount, + key: 'voucherGeneralAssistanceCount' + }, + { + label: + t.yearlyStatisticsColumns.voucherSpecialAssistanceCount, + key: 'voucherSpecialAssistanceCount' + }, + { + label: + t.yearlyStatisticsColumns + .voucherEnhancedAssistanceCount, + key: 'voucherEnhancedAssistanceCount' + }, + { + label: + t.yearlyStatisticsColumns + .municipalGeneralAssistanceCount, + key: 'municipalGeneralAssistanceCount' }, { label: t.yearlyStatisticsColumns - .specialAssistanceCount, - key: 'specialAssistanceCount' + .municipalSpecialAssistanceCount, + key: 'municipalSpecialAssistanceCount' }, { label: t.yearlyStatisticsColumns - .enhancedAssistanceCount, - key: 'enhancedAssistanceCount' + .municipalEnhancedAssistanceCount, + key: 'municipalEnhancedAssistanceCount' } ]} filename={`${t.reportLabel} ${selectedYear} - ${t.yearlyStatisticsReport}.csv`} diff --git a/frontend/src/lib-common/generated/api-types/reports.ts b/frontend/src/lib-common/generated/api-types/reports.ts index d9d765a648e..e1170309fe3 100644 --- a/frontend/src/lib-common/generated/api-types/reports.ts +++ b/frontend/src/lib-common/generated/api-types/reports.ts @@ -1166,16 +1166,19 @@ export type VoucherReportRowType = */ export interface YearlyStatisticsResult { club5YearOldCount: number - enhancedAssistanceCount: number familyCare5YearOldCount: number - generalAssistanceCount: number municipal5YearOldCount: number + municipalEnhancedAssistanceCount: number + municipalGeneralAssistanceCount: number + municipalSpecialAssistanceCount: number preschoolDaycareFamilyCareCount: number preschoolDaycareUnitCareCount: number purchased5YearOldCount: number - specialAssistanceCount: number voucher5YearOldCount: number voucherAssistanceCount: number + voucherEnhancedAssistanceCount: number + voucherGeneralAssistanceCount: number + voucherSpecialAssistanceCount: number voucherTotalCount: number } diff --git a/frontend/src/lib-customizations/defaults/employee/i18n/fi.tsx b/frontend/src/lib-customizations/defaults/employee/i18n/fi.tsx index fb2ea4a09b2..0968155eb46 100755 --- a/frontend/src/lib-customizations/defaults/employee/i18n/fi.tsx +++ b/frontend/src/lib-customizations/defaults/employee/i18n/fi.tsx @@ -4369,11 +4369,22 @@ export const fi = { municipal5YearOldCount: '5-vuotiaat kunnallisissa yksiköissä', familyCare5YearOldCount: '5-vuotiaat perhepäivähoidossa', club5YearOldCount: '5-vuotiaat kerhossa', - preschoolDaycareUnitCareCount: 'Täydentävän varhaiskasvatuksen lapset yksiköissä', - preschoolFamilyCareCount: 'Täydentävän varhaiskasvatuksen lapset perhepäivähoidossa', - generalAssistanceCount: 'Yleisen tuen lapsimäärä', - specialAssistanceCount: 'Erityisen tuen lapsimäärä', - enhancedAssistanceCount: 'Tehostetun tuen lapsimäärä', + preschoolDaycareUnitCareCount: + 'Täydentävän varhaiskasvatuksen lapset yksiköissä', + preschoolFamilyCareCount: + 'Täydentävän varhaiskasvatuksen lapset perhepäivähoidossa', + voucherGeneralAssistanceCount: + 'Yleisen tuen lapsimäärä (palveluseteli)', + voucherSpecialAssistanceCount: + 'Erityisen tuen lapsimäärä (palveluseteli)', + voucherEnhancedAssistanceCount: + 'Tehostetun tuen lapsimäärä (palveluseteli)', + municipalGeneralAssistanceCount: + 'Yleisen tuen lapsimäärä (kunnallinen)', + municipalSpecialAssistanceCount: + 'Erityisen tuen lapsimäärä (kunnallinen)', + municipalEnhancedAssistanceCount: + 'Tehostetun tuen lapsimäärä (kunnallinen)' } } }, diff --git a/service/src/integrationTest/kotlin/fi/espoo/evaka/reports/TampereRegionalSurveyTest.kt b/service/src/integrationTest/kotlin/fi/espoo/evaka/reports/TampereRegionalSurveyTest.kt index 6a967379a17..bdd9b096fd6 100644 --- a/service/src/integrationTest/kotlin/fi/espoo/evaka/reports/TampereRegionalSurveyTest.kt +++ b/service/src/integrationTest/kotlin/fi/espoo/evaka/reports/TampereRegionalSurveyTest.kt @@ -6,6 +6,7 @@ package fi.espoo.evaka.reports import fi.espoo.evaka.FullApplicationTest import fi.espoo.evaka.absence.AbsenceCategory +import fi.espoo.evaka.assistance.DaycareAssistanceLevel import fi.espoo.evaka.daycare.CareType import fi.espoo.evaka.daycare.domain.ProviderType import fi.espoo.evaka.placement.PlacementType @@ -978,6 +979,266 @@ class TampereRegionalSurveyTest : FullApplicationTest(resetDbBeforeEach = true) assertEquals(3, results.yearlyStatistics.first().voucherAssistanceCount) } + @Test + fun `Municipal assistance level counts are correct`() { + val octFirst = LocalDate.of(2024, 10, 1) + val testUnitData = initTestUnitData(octFirst) + val defaultPlacementDuration = + FiniteDateRange(octFirst, octFirst.plusMonths(3).minusDays(1)) + + val childTestData = + initTestPlacementData( + start = octFirst, + daycareId = testUnitData[0], + defaultPlacementDuration = defaultPlacementDuration, + baseAge = 5, + ) + + db.transaction { tx -> + // add daycare assistance periods that should show up for Aapo, Bertil and Ville + tx.insert( + DevDaycareAssistance( + childId = childTestData[0].first.id, + validDuring = + FiniteDateRange( + defaultPlacementDuration.start, + defaultPlacementDuration.end, + ), + level = DaycareAssistanceLevel.GENERAL_SUPPORT_WITH_DECISION + ) + ) + + tx.insert( + DevDaycareAssistance( + childId = childTestData[1].first.id, + validDuring = + FiniteDateRange( + defaultPlacementDuration.start, + defaultPlacementDuration.end, + ), + level = DaycareAssistanceLevel.INTENSIFIED_SUPPORT + ) + ) + + tx.insert( + DevPlacement( + childId = childTestData[3].first.id, + type = PlacementType.PRESCHOOL_DAYCARE_ONLY, + unitId = testUnitData[0], + startDate = defaultPlacementDuration.start, + endDate = defaultPlacementDuration.end, + ) + ) + tx.insert( + DevDaycareAssistance( + childId = childTestData[3].first.id, + validDuring = + FiniteDateRange( + defaultPlacementDuration.start, + defaultPlacementDuration.end, + ), + level = DaycareAssistanceLevel.SPECIAL_SUPPORT + ) + ) + + // add a daycare assistance period that should not show up for family care Fabio + tx.insert( + DevPlacement( + childId = childTestData[4].first.id, + type = PlacementType.DAYCARE, + unitId = testUnitData[2], + startDate = defaultPlacementDuration.start, + endDate = defaultPlacementDuration.end, + ) + ) + tx.insert( + DevDaycareAssistance( + childId = childTestData[4].first.id, + validDuring = + FiniteDateRange( + defaultPlacementDuration.start, + defaultPlacementDuration.end, + ), + level = DaycareAssistanceLevel.SPECIAL_SUPPORT + ) + ) + + // add a voucher daycare assistance period that should not show up + val testChildKaarina = + DevPerson( + dateOfBirth = startDate.minusYears(2), + firstName = "Kaarina", + lastName = "Kunnallinen", + language = "fi", + ) + + tx.insert(testChildKaarina, DevPersonType.CHILD) + tx.insert( + DevPlacement( + childId = testChildKaarina.id, + type = PlacementType.DAYCARE, + unitId = testUnitData[3], + startDate = defaultPlacementDuration.start, + endDate = defaultPlacementDuration.end, + ) + ) + tx.insert( + DevDaycareAssistance( + childId = testChildKaarina.id, + validDuring = + FiniteDateRange( + defaultPlacementDuration.start, + defaultPlacementDuration.end, + ), + level = DaycareAssistanceLevel.SPECIAL_SUPPORT + ) + ) + } + + val results = + tampereRegionalSurvey.getTampereRegionalSurveyYearlyStatistics( + dbInstance(), + adminLoginUser, + mockClock, + year = startDate.year, + ) + + // Aapo (GENERAL), Bertil (INTENSIFIED), Ville (SPECIAL) + assertEquals(1, results.yearlyStatistics.first().municipalGeneralAssistanceCount) + assertEquals(1, results.yearlyStatistics.first().municipalSpecialAssistanceCount) + assertEquals(1, results.yearlyStatistics.first().municipalEnhancedAssistanceCount) + } + + @Test + fun `Voucher assistance level counts are correct`() { + val octFirst = LocalDate.of(2024, 10, 1) + val testUnitData = initTestUnitData(octFirst) + val defaultPlacementDuration = + FiniteDateRange(octFirst, octFirst.plusMonths(3).minusDays(1)) + + val childTestData = + initTestPlacementData( + start = octFirst, + daycareId = testUnitData[3], + defaultPlacementDuration = defaultPlacementDuration, + baseAge = 5, + ) + + db.transaction { tx -> + // add daycare assistance periods that should show up for Aapo, Bertil and Ville + tx.insert( + DevDaycareAssistance( + childId = childTestData[0].first.id, + validDuring = + FiniteDateRange( + defaultPlacementDuration.start, + defaultPlacementDuration.end, + ), + level = DaycareAssistanceLevel.GENERAL_SUPPORT_WITH_DECISION + ) + ) + + tx.insert( + DevDaycareAssistance( + childId = childTestData[1].first.id, + validDuring = + FiniteDateRange( + defaultPlacementDuration.start, + defaultPlacementDuration.end, + ), + level = DaycareAssistanceLevel.INTENSIFIED_SUPPORT + ) + ) + + tx.insert( + DevPlacement( + childId = childTestData[3].first.id, + type = PlacementType.PRESCHOOL_DAYCARE_ONLY, + unitId = testUnitData[3], + startDate = defaultPlacementDuration.start, + endDate = defaultPlacementDuration.end, + ) + ) + tx.insert( + DevDaycareAssistance( + childId = childTestData[3].first.id, + validDuring = + FiniteDateRange( + defaultPlacementDuration.start, + defaultPlacementDuration.end, + ), + level = DaycareAssistanceLevel.SPECIAL_SUPPORT + ) + ) + + // add a daycare assistance period that should not show up for family care Fabio + tx.insert( + DevPlacement( + childId = childTestData[4].first.id, + type = PlacementType.DAYCARE, + unitId = testUnitData[2], + startDate = defaultPlacementDuration.start, + endDate = defaultPlacementDuration.end, + ) + ) + tx.insert( + DevDaycareAssistance( + childId = childTestData[4].first.id, + validDuring = + FiniteDateRange( + defaultPlacementDuration.start, + defaultPlacementDuration.end, + ), + level = DaycareAssistanceLevel.SPECIAL_SUPPORT + ) + ) + + // add a municipal daycare assistance period that should not show up + val testChildKaarina = + DevPerson( + dateOfBirth = startDate.minusYears(2), + firstName = "Kaarina", + lastName = "Kunnallinen", + language = "fi", + ) + + tx.insert(testChildKaarina, DevPersonType.CHILD) + tx.insert( + DevPlacement( + childId = testChildKaarina.id, + type = PlacementType.DAYCARE, + unitId = testUnitData[0], + startDate = defaultPlacementDuration.start, + endDate = defaultPlacementDuration.end, + ) + ) + tx.insert( + DevDaycareAssistance( + childId = testChildKaarina.id, + validDuring = + FiniteDateRange( + defaultPlacementDuration.start, + defaultPlacementDuration.end, + ), + level = DaycareAssistanceLevel.SPECIAL_SUPPORT + ) + ) + } + + val results = + tampereRegionalSurvey.getTampereRegionalSurveyYearlyStatistics( + dbInstance(), + adminLoginUser, + mockClock, + year = startDate.year, + ) + + // Aapo (GENERAL), Bertil (INTENSIFIED), Ville (SPECIAL) + assertEquals(1, results.yearlyStatistics.first().voucherGeneralAssistanceCount) + assertEquals(1, results.yearlyStatistics.first().voucherSpecialAssistanceCount) + assertEquals(1, results.yearlyStatistics.first().voucherEnhancedAssistanceCount) + } + private fun initTestUnitData(monday: LocalDate): List { return db.transaction { tx -> val areaAId = tx.insert(DevCareArea(name = "Area A", shortName = "Area A")) diff --git a/service/src/main/kotlin/fi/espoo/evaka/reports/TampereRegionalSurvey.kt b/service/src/main/kotlin/fi/espoo/evaka/reports/TampereRegionalSurvey.kt index db8e8ced839..abcea2eb102 100644 --- a/service/src/main/kotlin/fi/espoo/evaka/reports/TampereRegionalSurvey.kt +++ b/service/src/main/kotlin/fi/espoo/evaka/reports/TampereRegionalSurvey.kt @@ -5,6 +5,7 @@ package fi.espoo.evaka.reports import fi.espoo.evaka.Audit +import fi.espoo.evaka.assistance.DaycareAssistanceLevel import fi.espoo.evaka.shared.auth.AuthenticatedUser import fi.espoo.evaka.shared.db.Database import fi.espoo.evaka.shared.db.Predicate @@ -101,7 +102,7 @@ class TampereRegionalSurvey(private val accessControl: AccessControl) { clock: EvakaClock, @RequestParam year: Int, ): RegionalSurveyReportAgeStatisticsResult { - val yearlyStatDay = LocalDate.of(year, 12, 31) + val yearlyStatDay = LocalDate.of(year, 12, 15) val languageStatDay = LocalDate.of(year, 11, 30) return db.connect { dbc -> dbc.read { tx -> @@ -221,7 +222,7 @@ class TampereRegionalSurvey(private val accessControl: AccessControl) { clock: EvakaClock, @RequestParam year: Int, ): RegionalSurveyReportYearlyStatisticsResult { - val yearlyStatDay = LocalDate.of(year, 12, 31) + val yearlyStatDay = LocalDate.of(year, 12, 15) return db.connect { dbc -> dbc.read { tx -> accessControl.requirePermissionFor( @@ -236,7 +237,13 @@ class TampereRegionalSurvey(private val accessControl: AccessControl) { } val voucherDaycarePredicate = Predicate { - where("$it.provider_type = ANY ('{PRIVATE_SERVICE_VOUCHER}')") + where( + "$it.provider_type = ANY ('{PRIVATE_SERVICE_VOUCHER}') AND $it.type && '{CENTRE}'" + ) + } + + val municipalDaycarePredicate = Predicate { + where("$it.provider_type = ANY ('{MUNICIPAL}') AND $it.type && '{CENTRE}'") } val purchasedFiveYearOlds = @@ -333,35 +340,20 @@ class TampereRegionalSurvey(private val accessControl: AccessControl) { val voucherAssistance = tx.getYearlyAssistanceCount( - statDay = yearlyStatDay, - daycarePred = - voucherDaycarePredicate.and( - Predicate { where("$it.type && '{CENTRE}'") } - ), - ) - - val voucherGeneralAssistance = - tx.getYearlyAssistanceLevelCount( statDay = yearlyStatDay, daycarePred = voucherDaycarePredicate, - assistancePred = - Predicate { where("$it.level = ANY ('{GENERAL_SUPPORT}')") }, ) - val voucherSpecialAssistance = - tx.getYearlyAssistanceLevelCount( + val voucherAssistanceCounts = + tx.getYearlyAssistanceLevelCounts( statDay = yearlyStatDay, daycarePred = voucherDaycarePredicate, - assistancePred = - Predicate { where("$it.level = ANY ('{SPECIAL_SUPPORT}')") }, ) - val voucherEnhancedAssistance = - tx.getYearlyAssistanceLevelCount( + val municipalAssistanceCounts = + tx.getYearlyAssistanceLevelCounts( statDay = yearlyStatDay, - daycarePred = voucherDaycarePredicate, - assistancePred = - Predicate { where("$it.level = ANY ('{INTENSIFIED_SUPPORT}')") }, + daycarePred = municipalDaycarePredicate, ) RegionalSurveyReportYearlyStatisticsResult( @@ -380,12 +372,18 @@ class TampereRegionalSurvey(private val accessControl: AccessControl) { preschoolDaycareFamilyCare.placementCount, preschoolDaycareUnitCareCount = preschoolDaycareUnitCare.placementCount, - generalAssistanceCount = - voucherGeneralAssistance.placementCount, - specialAssistanceCount = - voucherSpecialAssistance.placementCount, - enhancedAssistanceCount = - voucherEnhancedAssistance.placementCount, + voucherGeneralAssistanceCount = + voucherAssistanceCounts.generalSupportWithDecisionCount, + voucherSpecialAssistanceCount = + voucherAssistanceCounts.specialSupportCount, + voucherEnhancedAssistanceCount = + voucherAssistanceCounts.intensifiedSupportCount, + municipalGeneralAssistanceCount = + municipalAssistanceCounts.generalSupportWithDecisionCount, + municipalSpecialAssistanceCount = + municipalAssistanceCounts.specialSupportCount, + municipalEnhancedAssistanceCount = + municipalAssistanceCounts.intensifiedSupportCount, ) ), ) @@ -468,9 +466,12 @@ class TampereRegionalSurvey(private val accessControl: AccessControl) { val familyCare5YearOldCount: Int, val preschoolDaycareUnitCareCount: Int, val preschoolDaycareFamilyCareCount: Int, - val generalAssistanceCount: Int, - val specialAssistanceCount: Int, - val enhancedAssistanceCount: Int, + val voucherGeneralAssistanceCount: Int, + val voucherSpecialAssistanceCount: Int, + val voucherEnhancedAssistanceCount: Int, + val municipalGeneralAssistanceCount: Int, + val municipalSpecialAssistanceCount: Int, + val municipalEnhancedAssistanceCount: Int, ) data class RegionalSurveyReportAgeStatisticsResult( @@ -487,6 +488,12 @@ class TampereRegionalSurvey(private val accessControl: AccessControl) { data class SingularCount(val placementCount: Int) + data class DaycareAssistanceLevelCounts( + val intensifiedSupportCount: Int, + val specialSupportCount: Int, + val generalSupportWithDecisionCount: Int, + ) + private fun Database.Read.getMonthlyMunicipalPlacementsByAgeAndPartTime( range: FiniteDateRange, statDays: List, @@ -780,27 +787,27 @@ WHERE ${predicate(placementPred.forTable("pl"))} .exactlyOne() } - private fun Database.Read.getYearlyAssistanceLevelCount( + private fun Database.Read.getYearlyAssistanceLevelCounts( statDay: LocalDate, daycarePred: Predicate, - assistancePred: Predicate, - ): SingularCount { + ): DaycareAssistanceLevelCounts { return createQuery { sql( """ -SELECT count(pl.child_id) as placement_count +SELECT count(CASE WHEN da.level = ${bind(DaycareAssistanceLevel.GENERAL_SUPPORT_WITH_DECISION)} THEN 1 END ) AS general_support_with_decision_count, + count(CASE WHEN da.level = ${bind(DaycareAssistanceLevel.SPECIAL_SUPPORT)} THEN 1 END ) AS special_support_count, + count(CASE WHEN da.level = ${bind(DaycareAssistanceLevel.INTENSIFIED_SUPPORT)} THEN 1 END ) AS intensified_support_count FROM placement pl JOIN daycare d ON pl.unit_id = d.id JOIN daycare_assistance da ON da.child_id = pl.child_id AND da.valid_during @> ${bind(statDay)} WHERE ${predicate(daycarePred.forTable("d"))} - AND ${predicate(assistancePred.forTable("da"))} - --AND pl.type = ANY ('{DAYCARE,PRESCHOOL_DAYCARE,PRESCHOOL_DAYCARE_ONLY}') + AND pl.type = ANY ('{DAYCARE,PRESCHOOL_DAYCARE,PRESCHOOL_DAYCARE_ONLY}') AND daterange(pl.start_date, pl.end_date, '[]') @> ${bind(statDay)} """ ) } - .exactlyOne() + .exactlyOne() } }