ContactHeuristicParserSpatialTest.kt
169 lignes · 6437 octets
package fr.ebii.card2vcf.contact import fr.ebii.card2vcf.ocr.OcrBox import fr.ebii.card2vcf.ocr.OcrLine import fr.ebii.card2vcf.ocr.OcrResult import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue class ContactHeuristicParserSpatialTest { private fun ligne( text: String, top: Int, height: Int = 30, left: Int = 40, right: Int = 500, confidence: Float = 90f, ) = OcrLine(text, OcrBox(left, top, right, top + height), confidence) private fun ocr( lines: List<OcrLine>, phones: List<String> = emptyList(), emails: List<String> = emptyList(), urls: List<String> = emptyList(), ) = OcrResult( rawText = lines.joinToString("\n") { it.text }, lines = lines.map { it.text }, phones = phones, emails = emails, urls = urls, spatialLines = lines, ) @Test fun adresseMultiLignesAssemblee() { val card = ContactHeuristicParser.parse( ocr( listOf( ligne("Sonia MARTIN", top = 100, height = 40), ligne("561 allée des Noisetiers", top = 400), ligne("69760 Limonest - France", top = 440), ), ), ) assertEquals("561 allée des Noisetiers, 69760 Limonest - France", card.address) } @Test fun nomEnGrosCaracteresSansMajusculesDetecte() { val card = ContactHeuristicParser.parse( ocr( listOf( ligne("sonia martin", top = 100, height = 64), ligne("directrice commerciale", top = 180, height = 28), ), emails = listOf("sonia.martin@voltea.fr"), ), ) assertEquals("Martin", card.lastName) assertEquals("sonia", card.firstName?.lowercase()) assertEquals("directrice commerciale", card.jobTitle) } @Test fun societeDominanteEnMajusculesSepareeDuNom() { val card = ContactHeuristicParser.parse( ocr( listOf( ligne("VOLTEA SOLUTIONS", top = 40, height = 50), ligne("Sonia MARTIN", top = 200, height = 34), ligne("Responsable grands comptes", top = 244, height = 28), ), emails = listOf("sonia.martin@voltea.fr"), ), ) assertEquals("VOLTEA SOLUTIONS", card.company) assertEquals("Sonia Martin", card.fullName) assertEquals("Responsable grands comptes", card.jobTitle) } @Test fun nomToutEnMajusculesResteUnNomPasUneSociete() { // Très courant en France : « SONIA MARTIN » tout en capitales. val card = ContactHeuristicParser.parse( ocr( listOf( ligne("SONIA MARTIN", top = 100, height = 34), ligne("Directrice commerciale", top = 150, height = 28), ), emails = listOf("sonia.martin@voltea.fr"), ), ) assertEquals("Sonia Martin", card.fullName) assertEquals("Martin", card.lastName) assertEquals("Voltea", card.company) // depuis le domaine email, pas le nom } @Test fun deuxColonnesSansMelangeAdresseTelephone() { val card = ContactHeuristicParser.parse( ocr( listOf( ligne("Sonia MARTIN", top = 60, height = 40, left = 40, right = 900), ligne("12 rue des Fleurs", top = 400, left = 40, right = 380), ligne("75011 Paris", top = 440, left = 40, right = 380), ligne("Tél. 04 75 35 12 34", top = 405, left = 600, right = 980), ligne("sonia@voltea.fr", top = 445, left = 600, right = 980), ), phones = listOf("0475351234"), emails = listOf("sonia@voltea.fr"), ), ) assertEquals("12 rue des Fleurs, 75011 Paris", card.address) assertEquals(listOf("0475351234"), card.phones) } @Test fun libelleTelephoneAvecValeurSurLaLigneSuivante() { val card = ContactHeuristicParser.parse( ocr( listOf( ligne("Sonia MARTIN", top = 60, height = 40), ligne("Tél. :", top = 300), ligne("04 75 35 12 34", top = 340), ), ), ) assertTrue(card.phones.contains("0475351234"), "téléphone attendu: ${card.phones}") } @Test fun champsDouteuxMarquesSousLeSeuilDeConfiance() { val card = ContactHeuristicParser.parse( ocr( listOf( ligne("Sonia MARTIN", top = 100, height = 40, confidence = 45f), ligne("561 allée des Noisetiers", top = 400, confidence = 92f), ligne("69760 Limonest - France", top = 440, confidence = 91f), ), ), ) assertTrue("fullName" in card.champsDouteux, "nom à 45% doit être douteux: ${card.champsDouteux}") assertTrue("address" !in card.champsDouteux, "adresse à ~91% doit être sûre: ${card.champsDouteux}") } @Test fun fallbackSansBoxesAucunChampDouteux() { val card = ContactHeuristicParser.parse( OcrResult(rawText = "Sonia MARTIN\nDirectrice", lines = listOf("Sonia MARTIN", "Directrice")), ) assertEquals(emptySet(), card.champsDouteux) } @Test fun cheminSpatialSeulementAvecBoxes() { val textes = listOf( "Sonia MARTIN", "561 allée des Noisetiers", "69760 Limonest - France", ) val avecBoxes = ContactHeuristicParser.parse( ocr( listOf( ligne(textes[0], top = 100, height = 40), ligne(textes[1], top = 400), ligne(textes[2], top = 440), ), ), ) val sansBoxes = ContactHeuristicParser.parse( OcrResult(rawText = textes.joinToString("\n"), lines = textes), ) // Spatial : adresse complète ; legacy : une seule ligne (la plus longue). assertEquals("561 allée des Noisetiers, 69760 Limonest - France", avecBoxes.address) assertEquals("561 allée des Noisetiers", sansBoxes.address) } }
GitRust