Spaces:
Sleeping
Sleeping
| from difflib import SequenceMatcher | |
| from app.schemas.document_verification import TextConsistencyAnalysis | |
| from app.utils.text_utils import normalize_text | |
| class TextConsistencyChecker: | |
| def compare(self, pdf_text: str | None, ocr_text: str | None) -> TextConsistencyAnalysis: | |
| normalized_pdf = normalize_text(pdf_text) | |
| normalized_ocr = normalize_text(ocr_text) | |
| if not normalized_pdf or not normalized_ocr: | |
| return TextConsistencyAnalysis( | |
| checked=True, | |
| similarity_score=0.0, | |
| risk_score=0.0, | |
| status="no_text_to_compare", | |
| flags=[], | |
| warnings=["PDF text or OCR text was unavailable, so consistency could not be compared."], | |
| ) | |
| similarity = self._similarity(normalized_pdf, normalized_ocr) | |
| flags: list[str] = [] | |
| warnings: list[str] = [] | |
| if similarity >= 0.85: | |
| status = "strong_match" | |
| risk = 0.05 | |
| elif similarity >= 0.60: | |
| status = "partial_match" | |
| risk = 0.35 | |
| flags.append("partial_text_mismatch") | |
| warnings.append("Embedded PDF text and visible OCR text only partially match.") | |
| else: | |
| status = "low_match" | |
| risk = 0.70 | |
| flags.append("low_text_match") | |
| warnings.append("Embedded PDF text and visible OCR text diverge significantly.") | |
| return TextConsistencyAnalysis( | |
| checked=True, | |
| similarity_score=round(similarity, 2), | |
| risk_score=risk, | |
| status=status, | |
| flags=flags, | |
| warnings=warnings, | |
| ) | |
| def _similarity(self, left: str, right: str) -> float: | |
| try: | |
| from rapidfuzz import fuzz | |
| return fuzz.token_set_ratio(left, right) / 100 | |
| except Exception: | |
| return SequenceMatcher(None, left, right).ratio() | |