Queue guest document extraction
All checks were successful
build-and-deploy / build-deploy (push) Successful in 3m36s
All checks were successful
build-and-deploy / build-deploy (push) Successful in 3m36s
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.android.trisolarisserver.component
|
||||
|
||||
import jakarta.annotation.PreDestroy
|
||||
import org.springframework.stereotype.Component
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
@Component
|
||||
class ExtractionQueue {
|
||||
private val executor = Executors.newSingleThreadExecutor { runnable ->
|
||||
Thread(runnable, "doc-extraction-queue").apply { isDaemon = true }
|
||||
}
|
||||
|
||||
fun enqueue(task: () -> Unit) {
|
||||
executor.submit {
|
||||
try {
|
||||
task()
|
||||
} catch (_: Exception) {
|
||||
// Best-effort processing; failures should not crash the worker.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
fun shutdown() {
|
||||
executor.shutdown()
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.android.trisolarisserver.controller
|
||||
|
||||
import com.android.trisolarisserver.component.DocumentStorage
|
||||
import com.android.trisolarisserver.component.DocumentTokenService
|
||||
import com.android.trisolarisserver.component.ExtractionQueue
|
||||
import com.android.trisolarisserver.component.LlamaClient
|
||||
import com.android.trisolarisserver.component.PropertyAccess
|
||||
import com.android.trisolarisserver.db.repo.BookingRepo
|
||||
@@ -38,6 +39,7 @@ class GuestDocuments(
|
||||
private val appUserRepo: AppUserRepo,
|
||||
private val storage: DocumentStorage,
|
||||
private val tokenService: DocumentTokenService,
|
||||
private val extractionQueue: ExtractionQueue,
|
||||
private val llamaClient: LlamaClient,
|
||||
private val objectMapper: ObjectMapper,
|
||||
@org.springframework.beans.factory.annotation.Value("\${storage.documents.publicBaseUrl}")
|
||||
@@ -88,8 +90,8 @@ class GuestDocuments(
|
||||
storagePath = stored.storagePath
|
||||
)
|
||||
val saved = guestDocumentRepo.save(document)
|
||||
runExtraction(saved, propertyId, guestId)
|
||||
return guestDocumentRepo.save(saved).toResponse(objectMapper)
|
||||
runExtraction(saved.id!!, propertyId, guestId)
|
||||
return saved.toResponse(objectMapper)
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
@@ -135,63 +137,67 @@ class GuestDocuments(
|
||||
.body(resource)
|
||||
}
|
||||
|
||||
private fun runExtraction(document: GuestDocument, propertyId: UUID, guestId: UUID) {
|
||||
try {
|
||||
val token = tokenService.createToken(document.id.toString())
|
||||
val imageUrl =
|
||||
"${publicBaseUrl}/properties/$propertyId/guests/$guestId/documents/${document.id}/file?token=$token"
|
||||
private fun runExtraction(documentId: UUID, propertyId: UUID, guestId: UUID) {
|
||||
extractionQueue.enqueue {
|
||||
val document = guestDocumentRepo.findById(documentId).orElse(null) ?: return@enqueue
|
||||
try {
|
||||
val token = tokenService.createToken(document.id.toString())
|
||||
val imageUrl =
|
||||
"${publicBaseUrl}/properties/$propertyId/guests/$guestId/documents/${document.id}/file?token=$token"
|
||||
|
||||
val results = linkedMapOf<String, String>()
|
||||
results["hasAadhar"] = llamaClient.ask(imageUrl, "CONTAINS AADHAAR? Answer YES or NO only.")
|
||||
results["hasUidai"] = llamaClient.ask(imageUrl, "CONTAINS UIDAI? Answer YES or NO only.")
|
||||
results["hasTransportDept"] = llamaClient.ask(imageUrl, "CONTAINS TRANSPORT DEPARTMENT? Answer YES or NO only.")
|
||||
results["hasIncomeTaxDept"] = llamaClient.ask(imageUrl, "CONTAINS INCOME TAX DEPARTMENT? Answer YES or NO only.")
|
||||
results["hasElectionCommission"] = llamaClient.ask(imageUrl, "CONTAINS ELECTION COMMISSION OF INDIA? Answer YES or NO only.")
|
||||
results["hasDrivingLicence"] = llamaClient.ask(imageUrl, "CONTAINS DRIVING LICENCE? Answer YES or NO only.")
|
||||
results["hasPassport"] = llamaClient.ask(imageUrl, "CONTAINS PASSPORT? Answer YES or NO only.")
|
||||
results["hasPolice"] = llamaClient.ask(imageUrl, "CONTAINS POLICE? Answer YES or NO only.")
|
||||
results["hasCourt"] = llamaClient.ask(imageUrl, "CONTAINS COURT? Answer YES or NO only.")
|
||||
results["hasHighCourt"] = llamaClient.ask(imageUrl, "CONTAINS HIGH COURT? Answer YES or NO only.")
|
||||
results["hasSupremeCourt"] = llamaClient.ask(imageUrl, "CONTAINS SUPREME COURT? Answer YES or NO only.")
|
||||
results["hasJudiciary"] = llamaClient.ask(imageUrl, "CONTAINS JUDICIARY? Answer YES or NO only.")
|
||||
results["hasAddress"] = llamaClient.ask(imageUrl, "ADDRESS PRESENT? Answer YES or NO only.")
|
||||
results["hasGender"] = llamaClient.ask(imageUrl, "GENDER PRESENT? Answer YES or NO only.")
|
||||
results["hasNationality"] = llamaClient.ask(imageUrl, "NATIONALITY PRESENT? Answer YES or NO only.")
|
||||
results["name"] = llamaClient.ask(imageUrl, "NAME? Reply only the name or NONE.")
|
||||
results["dob"] = llamaClient.ask(imageUrl, "DOB? Reply only date or NONE.")
|
||||
results["idNumber"] = llamaClient.ask(imageUrl, "ID NUMBER? Reply only number or NONE.")
|
||||
results["address"] = llamaClient.ask(imageUrl, "ADDRESS? Reply only address or NONE.")
|
||||
results["vehicleNumber"] = llamaClient.ask(imageUrl, "VEHICLE NUMBER? Reply only number or NONE.")
|
||||
results["isVehiclePhoto"] = llamaClient.ask(imageUrl, "IS THIS A VEHICLE PHOTO? Answer YES or NO only.")
|
||||
results["pinCode"] = llamaClient.ask(imageUrl, "PIN CODE? Reply only pin or NONE.")
|
||||
results["city"] = llamaClient.ask(imageUrl, "CITY? Reply only city or NONE.")
|
||||
results["gender"] = llamaClient.ask(imageUrl, "GENDER? Reply only MALE/FEMALE/OTHER or NONE.")
|
||||
results["nationality"] = llamaClient.ask(imageUrl, "NATIONALITY? Reply only nationality or NONE.")
|
||||
val results = linkedMapOf<String, String>()
|
||||
results["hasAadhar"] = llamaClient.ask(imageUrl, "CONTAINS AADHAAR? Answer YES or NO only.")
|
||||
results["hasUidai"] = llamaClient.ask(imageUrl, "CONTAINS UIDAI? Answer YES or NO only.")
|
||||
results["hasTransportDept"] = llamaClient.ask(imageUrl, "CONTAINS TRANSPORT DEPARTMENT? Answer YES or NO only.")
|
||||
results["hasIncomeTaxDept"] = llamaClient.ask(imageUrl, "CONTAINS INCOME TAX DEPARTMENT? Answer YES or NO only.")
|
||||
results["hasElectionCommission"] = llamaClient.ask(imageUrl, "CONTAINS ELECTION COMMISSION OF INDIA? Answer YES or NO only.")
|
||||
results["hasDrivingLicence"] = llamaClient.ask(imageUrl, "CONTAINS DRIVING LICENCE? Answer YES or NO only.")
|
||||
results["hasPassport"] = llamaClient.ask(imageUrl, "CONTAINS PASSPORT? Answer YES or NO only.")
|
||||
results["hasPolice"] = llamaClient.ask(imageUrl, "CONTAINS POLICE? Answer YES or NO only.")
|
||||
results["hasCourt"] = llamaClient.ask(imageUrl, "CONTAINS COURT? Answer YES or NO only.")
|
||||
results["hasHighCourt"] = llamaClient.ask(imageUrl, "CONTAINS HIGH COURT? Answer YES or NO only.")
|
||||
results["hasSupremeCourt"] = llamaClient.ask(imageUrl, "CONTAINS SUPREME COURT? Answer YES or NO only.")
|
||||
results["hasJudiciary"] = llamaClient.ask(imageUrl, "CONTAINS JUDICIARY? Answer YES or NO only.")
|
||||
results["hasAddress"] = llamaClient.ask(imageUrl, "ADDRESS PRESENT? Answer YES or NO only.")
|
||||
results["hasGender"] = llamaClient.ask(imageUrl, "GENDER PRESENT? Answer YES or NO only.")
|
||||
results["hasNationality"] = llamaClient.ask(imageUrl, "NATIONALITY PRESENT? Answer YES or NO only.")
|
||||
results["name"] = llamaClient.ask(imageUrl, "NAME? Reply only the name or NONE.")
|
||||
results["dob"] = llamaClient.ask(imageUrl, "DOB? Reply only date or NONE.")
|
||||
results["idNumber"] = llamaClient.ask(imageUrl, "ID NUMBER? Reply only number or NONE.")
|
||||
results["address"] = llamaClient.ask(imageUrl, "ADDRESS? Reply only address or NONE.")
|
||||
results["vehicleNumber"] = llamaClient.ask(imageUrl, "VEHICLE NUMBER? Reply only number or NONE.")
|
||||
results["isVehiclePhoto"] = llamaClient.ask(imageUrl, "IS THIS A VEHICLE PHOTO? Answer YES or NO only.")
|
||||
results["pinCode"] = llamaClient.ask(imageUrl, "PIN CODE? Reply only pin or NONE.")
|
||||
results["city"] = llamaClient.ask(imageUrl, "CITY? Reply only city or NONE.")
|
||||
results["gender"] = llamaClient.ask(imageUrl, "GENDER? Reply only MALE/FEMALE/OTHER or NONE.")
|
||||
results["nationality"] = llamaClient.ask(imageUrl, "NATIONALITY? Reply only nationality or NONE.")
|
||||
|
||||
results["docType"] = when {
|
||||
results["hasCourt"].orEmpty().contains("YES", ignoreCase = true) ||
|
||||
results["hasHighCourt"].orEmpty().contains("YES", ignoreCase = true) ||
|
||||
results["hasSupremeCourt"].orEmpty().contains("YES", ignoreCase = true) ||
|
||||
results["hasJudiciary"].orEmpty().contains("YES", ignoreCase = true) -> "COURT_ID"
|
||||
results["hasPolice"].orEmpty().contains("YES", ignoreCase = true) -> "POLICE_ID"
|
||||
results["hasPassport"].orEmpty().contains("YES", ignoreCase = true) -> "PASSPORT"
|
||||
results["hasTransportDept"].orEmpty().contains("YES", ignoreCase = true) ||
|
||||
results["hasDrivingLicence"].orEmpty().contains("YES", ignoreCase = true) -> "TRANSPORT"
|
||||
results["hasIncomeTaxDept"].orEmpty().contains("YES", ignoreCase = true) -> "PAN"
|
||||
results["hasElectionCommission"].orEmpty().contains("YES", ignoreCase = true) -> "VOTER_ID"
|
||||
results["hasAadhar"].orEmpty().contains("YES", ignoreCase = true) ||
|
||||
results["hasUidai"].orEmpty().contains("YES", ignoreCase = true) -> {
|
||||
if (results["hasAddress"].orEmpty().contains("YES", ignoreCase = true)) "AADHAR_BACK" else "AADHAR_FRONT"
|
||||
results["docType"] = when {
|
||||
results["hasCourt"].orEmpty().contains("YES", ignoreCase = true) ||
|
||||
results["hasHighCourt"].orEmpty().contains("YES", ignoreCase = true) ||
|
||||
results["hasSupremeCourt"].orEmpty().contains("YES", ignoreCase = true) ||
|
||||
results["hasJudiciary"].orEmpty().contains("YES", ignoreCase = true) -> "COURT_ID"
|
||||
results["hasPolice"].orEmpty().contains("YES", ignoreCase = true) -> "POLICE_ID"
|
||||
results["hasPassport"].orEmpty().contains("YES", ignoreCase = true) -> "PASSPORT"
|
||||
results["hasTransportDept"].orEmpty().contains("YES", ignoreCase = true) ||
|
||||
results["hasDrivingLicence"].orEmpty().contains("YES", ignoreCase = true) -> "TRANSPORT"
|
||||
results["hasIncomeTaxDept"].orEmpty().contains("YES", ignoreCase = true) -> "PAN"
|
||||
results["hasElectionCommission"].orEmpty().contains("YES", ignoreCase = true) -> "VOTER_ID"
|
||||
results["hasAadhar"].orEmpty().contains("YES", ignoreCase = true) ||
|
||||
results["hasUidai"].orEmpty().contains("YES", ignoreCase = true) -> {
|
||||
if (results["hasAddress"].orEmpty().contains("YES", ignoreCase = true)) "AADHAR_BACK" else "AADHAR_FRONT"
|
||||
}
|
||||
results["vehicleNumber"].orEmpty().isNotBlank() && !results["vehicleNumber"]!!.contains("NONE", true) -> "VEHICLE"
|
||||
results["isVehiclePhoto"].orEmpty().contains("YES", ignoreCase = true) -> "VEHICLE_PHOTO"
|
||||
else -> "UNKNOWN"
|
||||
}
|
||||
results["vehicleNumber"].orEmpty().isNotBlank() && !results["vehicleNumber"]!!.contains("NONE", true) -> "VEHICLE"
|
||||
results["isVehiclePhoto"].orEmpty().contains("YES", ignoreCase = true) -> "VEHICLE_PHOTO"
|
||||
else -> "UNKNOWN"
|
||||
}
|
||||
|
||||
document.extractedData = objectMapper.writeValueAsString(results)
|
||||
document.extractedAt = OffsetDateTime.now()
|
||||
} catch (_: Exception) {
|
||||
// Keep upload successful even if AI extraction fails.
|
||||
document.extractedData = objectMapper.writeValueAsString(results)
|
||||
document.extractedAt = OffsetDateTime.now()
|
||||
guestDocumentRepo.save(document)
|
||||
} catch (_: Exception) {
|
||||
// Keep upload successful even if AI extraction fails.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user