From e68e7c685c77a5c47eb73f4ef7e06e0d624d3d28 Mon Sep 17 00:00:00 2001 From: androidlover5842 Date: Sun, 1 Feb 2026 01:51:20 +0530 Subject: [PATCH] Compute age from valid DOB in extraction --- .../component/DocumentExtractionService.kt | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/main/kotlin/com/android/trisolarisserver/component/DocumentExtractionService.kt b/src/main/kotlin/com/android/trisolarisserver/component/DocumentExtractionService.kt index 7609904..aadb994 100644 --- a/src/main/kotlin/com/android/trisolarisserver/component/DocumentExtractionService.kt +++ b/src/main/kotlin/com/android/trisolarisserver/component/DocumentExtractionService.kt @@ -11,6 +11,12 @@ import com.android.trisolarisserver.repo.PropertyRepo import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.ObjectMapper import java.time.OffsetDateTime +import java.time.LocalDate +import java.time.Period +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeFormatterBuilder +import java.time.format.ResolverStyle import java.util.UUID import org.slf4j.LoggerFactory @@ -268,6 +274,7 @@ class DocumentExtractionService( normalizeIdNumber(results) logIdNumber("after-normalize-id-digits", document.id, results) normalizeAddress(results) + computeAgeIfDobPresent(results, propertyId) applyBookingCityUpdates(document, results) // Final Aadhaar checksum pass before doc type decision. markAadhaarIfValid(results) @@ -317,6 +324,18 @@ class DocumentExtractionService( results[key] = normalized } + private fun computeAgeIfDobPresent(results: MutableMap, propertyId: UUID) { + val dobRaw = cleanedValue(results[DocumentPrompts.DOB.first]) ?: return + val dob = parseDob(dobRaw) ?: return + val property = propertyRepo.findById(propertyId).orElse(null) ?: return + val zone = runCatching { ZoneId.of(property.timezone) }.getOrNull() ?: ZoneId.systemDefault() + val today = LocalDate.now(zone) + val years = Period.between(dob, today).years + if (years in 0..120) { + results["age"] = years.toString() + } + } + private fun markAadhaarIfValid(results: MutableMap) { val idKey = DocumentPrompts.ID_NUMBER.first val digits = normalizeDigits(cleanedValue(results[idKey])) @@ -696,6 +715,24 @@ private fun unorderedMatchCount(a: String, b: String): Int { } return total } + +private fun parseDob(value: String): LocalDate? { + val cleaned = value.trim().replace(Regex("\\s+"), "") + val formatters = listOf( + DateTimeFormatterBuilder().parseCaseInsensitive().appendPattern("dd/MM/uuuu").toFormatter(), + DateTimeFormatterBuilder().parseCaseInsensitive().appendPattern("dd-MM-uuuu").toFormatter(), + DateTimeFormatterBuilder().parseCaseInsensitive().appendPattern("uuuu-MM-dd").toFormatter(), + DateTimeFormatterBuilder().parseCaseInsensitive().appendPattern("uuuu/MM/dd").toFormatter() + ).map { it.withResolverStyle(ResolverStyle.STRICT) } + + for (formatter in formatters) { + try { + return LocalDate.parse(cleaned, formatter) + } catch (_: Exception) { + } + } + return null +} private fun extractPinFromValue(value: String?): String? { if (value.isNullOrBlank()) return null val compact = value.replace(Regex("\\s+"), "")