From 602fb2c5ddfe492a675e3a6a6f911ec50f4a738f Mon Sep 17 00:00:00 2001 From: andreia Date: Tue, 28 Jul 2026 16:43:35 +0200 Subject: [PATCH 1/2] replace timestamp with submission id for pdf file name --- .../feature/pdf/mapper/LoiReportMapper.kt | 8 +-- .../feature/pdf/mapper/LoiReportMapperTest.kt | 55 ++++++++++++------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt b/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt index 970f49c839..bd4e60532e 100644 --- a/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt +++ b/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt @@ -62,15 +62,13 @@ class LoiReportMapper( ), mapBlock = buildMapBlock(details), ) - val dateMillis = submission.lastModified.clientTimestamp - val timestamp = - "${dateFormatter.formatDate(dateMillis)}_${dateFormatter.formatTime(dateMillis)}" + val fileName = - listOf(details.surveyName, loiReport.loiName, details.userName, timestamp) + listOf(details.surveyName, loiReport.loiName, details.userName) .map { it.filter(::isSafeFileChar) } .filter { it.isNotBlank() } .joinToString("_") - .take(100) + .take(100) + "_${submission.id}" return PdfExportService.Request( document = document, diff --git a/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt b/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt index 5aeae233af..08b8131d41 100644 --- a/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt +++ b/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt @@ -18,6 +18,7 @@ package org.groundplatform.feature.pdf.mapper import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull +import kotlin.test.assertTrue import kotlinx.coroutines.test.runTest import org.groundplatform.domain.model.geometry.Coordinates import org.groundplatform.domain.model.geometry.LinearRing @@ -46,7 +47,7 @@ class LoiReportMapperTest { ) @Test - fun `file name joins survey loi user and timestamp with underscores`() = runTest { + fun `file name joins survey loi user and submission id with underscores`() = runTest { val request = mapper.map( loiReport = @@ -58,7 +59,7 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals("Survey_Loi_User_$TEST_TIMESTAMP", request!!.fileName) + assertEquals("Survey_Loi_User_$TEST_SUBMISSION_ID", request!!.fileName) } @Test @@ -77,7 +78,7 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals("MySurvey_loiname_useremailcom_$TEST_TIMESTAMP", request!!.fileName) + assertEquals("MySurvey_loiname_useremailcom_$TEST_SUBMISSION_ID", request!!.fileName) } @Test @@ -93,7 +94,7 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals("loi_$TEST_TIMESTAMP", request!!.fileName) + assertEquals("loi_$TEST_SUBMISSION_ID", request!!.fileName) } @Test @@ -109,7 +110,7 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals("แบบสำรวจ_ເພີ່ມຈຸດສຳຫຼວດ_テスト_$TEST_TIMESTAMP", request!!.fileName) + assertEquals("แบบสำรวจ_ເພີ່ມຈຸດສຳຫຼວດ_テスト_$TEST_SUBMISSION_ID", request!!.fileName) } @Test @@ -128,11 +129,11 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals("CaféSãoJosé_ß_Test_$TEST_TIMESTAMP", request!!.fileName) + assertEquals("CaféSãoJosé_ß_Test_$TEST_SUBMISSION_ID", request!!.fileName) } @Test - fun `file name is capped at 100 characters`() = runTest { + fun `file name is capped at 100 characters and still ends with the submission id`() = runTest { val request = mapper.map( loiReport = @@ -144,26 +145,39 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals(100, request!!.fileName.length) + val fileName = request!!.fileName + assertEquals(100, fileName.removeSuffix("_$TEST_SUBMISSION_ID").length) + assertTrue(fileName.endsWith("_$TEST_SUBMISSION_ID")) } @Test - fun `timestamp comes from the submission's own last modified date`() = runTest { + fun `submissions sharing survey, loi name and user get different file names`() = runTest { + val loiReport = + FakeDataGenerator.newLoiReport( + loiName = "Loi", + submissionDetails = + FakeDataGenerator.newSubmissionDetails(surveyName = "Survey", userName = "User"), + ) + + val first = mapper.map(loiReport, FakeDataGenerator.newSubmission(id = "a")) + val second = mapper.map(loiReport, FakeDataGenerator.newSubmission(id = "b")) + + assertEquals("Survey_Loi_User_a", first!!.fileName) + assertEquals("Survey_Loi_User_b", second!!.fileName) + } + + @Test + fun `header timestamp comes from the submission's own last modified date`() = runTest { val request = mapper.map( - loiReport = - FakeDataGenerator.newLoiReport( - loiName = "Loi", - submissionDetails = - FakeDataGenerator.newSubmissionDetails(surveyName = "Survey", userName = "User"), - ), + loiReport = FakeDataGenerator.newLoiReport(), submission = FakeDataGenerator.newSubmission( lastModified = AuditInfo(FakeDataGenerator.newUser(), clientTimestamp = 42L) ), ) - assertEquals("Survey_Loi_User_DATE42_TIME42", request!!.fileName) + assertEquals("DATE(42) TIME(42)", request!!.document.header.timestamp) } @Test @@ -234,11 +248,14 @@ class LoiReportMapperTest { ) ) ) - const val TEST_TIMESTAMP = "DATE0_TIME0" - /** Submission with a fixed last-modified timestamp so date assertions are deterministic. */ + const val TEST_SUBMISSION_ID = "submissionId" + /** + * Submission with a fixed id and last-modified timestamp so name assertions are deterministic. + */ val submission = FakeDataGenerator.newSubmission( - lastModified = AuditInfo(FakeDataGenerator.newUser(), clientTimestamp = 0L) + id = TEST_SUBMISSION_ID, + lastModified = AuditInfo(FakeDataGenerator.newUser(), clientTimestamp = 0L), ) } } From 319a934f0ddf91094b864db569d97aec4fd82ad0 Mon Sep 17 00:00:00 2001 From: andreia Date: Tue, 28 Jul 2026 16:59:07 +0200 Subject: [PATCH 2/2] reduce char count for naming --- .../feature/pdf/mapper/LoiReportMapper.kt | 2 +- .../feature/pdf/mapper/LoiReportMapperTest.kt | 26 +++++++++++++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt b/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt index bd4e60532e..764a65f263 100644 --- a/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt +++ b/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt @@ -68,7 +68,7 @@ class LoiReportMapper( .map { it.filter(::isSafeFileChar) } .filter { it.isNotBlank() } .joinToString("_") - .take(100) + "_${submission.id}" + .take(60) + "_${submission.id}" return PdfExportService.Request( document = document, diff --git a/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt b/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt index 08b8131d41..13a1674485 100644 --- a/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt +++ b/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt @@ -133,7 +133,7 @@ class LoiReportMapperTest { } @Test - fun `file name is capped at 100 characters and still ends with the submission id`() = runTest { + fun `file name is capped at 60 characters and still ends with the submission id`() = runTest { val request = mapper.map( loiReport = @@ -146,10 +146,29 @@ class LoiReportMapperTest { ) val fileName = request!!.fileName - assertEquals(100, fileName.removeSuffix("_$TEST_SUBMISSION_ID").length) + assertEquals(60, fileName.removeSuffix("_$TEST_SUBMISSION_ID").length) assertTrue(fileName.endsWith("_$TEST_SUBMISSION_ID")) } + @Test + fun `file name stays within the file system byte limit for multi-byte scripts`() = runTest { + val request = + mapper.map( + loiReport = + FakeDataGenerator.newLoiReport( + loiName = "ເພີ່ມຈຸດສຳຫຼວດ".repeat(20), + submissionDetails = + FakeDataGenerator.newSubmissionDetails( + surveyName = "แบบสำรวจ".repeat(20), + userName = "テスト".repeat(20), + ), + ), + submission = submission, + ) + + assertTrue(request!!.fileName.encodeToByteArray().size <= 255) + } + @Test fun `submissions sharing survey, loi name and user get different file names`() = runTest { val loiReport = @@ -249,9 +268,6 @@ class LoiReportMapperTest { ) ) const val TEST_SUBMISSION_ID = "submissionId" - /** - * Submission with a fixed id and last-modified timestamp so name assertions are deterministic. - */ val submission = FakeDataGenerator.newSubmission( id = TEST_SUBMISSION_ID,