From 8df2b1dcc186431f289ecad350945e053d132ed8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 5 Sep 2026 19:51:03 +0000 Subject: [PATCH 1/2] Initial plan From 9e3d70075e3c239073abc5e374ccbaea59b17534 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 5 Sep 2026 19:55:00 +0000 Subject: [PATCH 2/2] Fix CI install step and restore missing test modules Co-authored-by: M-O-J-U <102480072+M-O-J-U@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- docling_pk/ocr.py | 16 ++++++++++++ docling_pk/parsers/__init__.py | 3 +++ .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 281 bytes .../parsers/__pycache__/cnic.cpython-312.pyc | Bin 0 -> 1246 bytes .../__pycache__/intermediate.cpython-312.pyc | Bin 0 -> 330 bytes .../__pycache__/matric.cpython-312.pyc | Bin 0 -> 324 bytes docling_pk/parsers/_federal_board.py | 5 ++++ docling_pk/parsers/cnic.py | 20 +++++++++++++++ docling_pk/parsers/intermediate.py | 2 ++ docling_pk/parsers/matric.py | 2 ++ docling_pk/preprocessor.py | 24 ++++++++++++++++++ 12 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 docling_pk/ocr.py create mode 100644 docling_pk/parsers/__init__.py create mode 100644 docling_pk/parsers/__pycache__/__init__.cpython-312.pyc create mode 100644 docling_pk/parsers/__pycache__/cnic.cpython-312.pyc create mode 100644 docling_pk/parsers/__pycache__/intermediate.cpython-312.pyc create mode 100644 docling_pk/parsers/__pycache__/matric.cpython-312.pyc create mode 100644 docling_pk/parsers/_federal_board.py create mode 100644 docling_pk/parsers/cnic.py create mode 100644 docling_pk/parsers/intermediate.py create mode 100644 docling_pk/parsers/matric.py create mode 100644 docling_pk/preprocessor.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 396e2c0..4900b39 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r requirements.txt + pip install ".[dev]" - name: Run tests run: pytest -v --cov=docling_pk --cov-report=term-missing \ No newline at end of file diff --git a/docling_pk/ocr.py b/docling_pk/ocr.py new file mode 100644 index 0000000..5f51215 --- /dev/null +++ b/docling_pk/ocr.py @@ -0,0 +1,16 @@ +from typing import Iterable + +import numpy as np + + +def run_ocr(image: np.ndarray, languages: Iterable[str] = ("en",)) -> tuple[str, float]: + import easyocr + + reader = easyocr.Reader(list(languages), gpu=False) + results = reader.readtext(image) + + text_parts = [item[1] for item in results] + confidences = [float(item[2]) for item in results] + avg_confidence = sum(confidences) / len(confidences) if confidences else 0.0 + + return "\n".join(text_parts), avg_confidence diff --git a/docling_pk/parsers/__init__.py b/docling_pk/parsers/__init__.py new file mode 100644 index 0000000..bcc5cba --- /dev/null +++ b/docling_pk/parsers/__init__.py @@ -0,0 +1,3 @@ +from . import cnic, intermediate, matric + +__all__ = ["cnic", "matric", "intermediate"] diff --git a/docling_pk/parsers/__pycache__/__init__.cpython-312.pyc b/docling_pk/parsers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..780ab0bc705b7c15eb446f23415e6de197d4ca88 GIT binary patch literal 281 zcmZuryH3ME5Zv`S1Ok!x1j<+f?gtPhB`uPhmE}8`gS)e}c7#&$6*N?IdilB8Y5z2#mLx#$L>({8odJqxW_dV9|Ma(4W- WKNR{>i1-GJS6IByj_;?uC*c;3Oh^#` literal 0 HcmV?d00001 diff --git a/docling_pk/parsers/__pycache__/cnic.cpython-312.pyc b/docling_pk/parsers/__pycache__/cnic.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..81969ba410c53b1ed7ed3f2ba0f2c11311c86377 GIT binary patch literal 1246 zcma)5OK1~Y7(Vk%nzrg>Kx=#!D`dnrq7_BNTyJl#s3liY5lvG|GUp^soJ_)*iH|yk zEUw^P8CSgv7vZj|b>UKXcBkBXF(HNMu^SN=f=HyUTzO8C@r8mv%>VfQ*E#=x=HLE) zAE0!>x|Lhv0eHy_m*lRpTA|83fBT%M2f9N+;0IXBMDqY?^>T*r)yKa3G+ zZoB&ziBw&r=U^0dll9XNc0ovx4N04^T1|Un{o{Oh8%F2v*?&0=Z<5Jvqd-PCo_15 z!-~V@JZ|1+33<1!$w4qDI6U6w@ZXL6w2)lS=X_UN>?-XlqTTD_j^GHGha$zLyRy5O zFZ`k-QorYGKlI+>CxF%yB>=NszGm~wq}%j1oNoa7{u}Ji{NbD0XWpJTX zBvPqZL>RN(XcA_lr`=fDeA1>_eVwXZtK;dM$@R1?i*0>7qYuA^W(s)}!X?v0ICQCi zFN9!0H?n4CxcEJFnxe%dwh*>LtnocR5(i{Rc$ps5VjmBH%ZvNCjEET4Eb`b*_- zQ{7otcUF@PHP}=~>gvd{y5}J*kH43}z>bB$W8ackJ3G0&A+h4$QXO9!e=t!yb8gum ze-EK0|+!_8f^vFXW3^1L1xeOMJ(VMgNFJ#*R)z z@LC24mW_!Bv)%jM26%wNcs*qwyod7zSTfKNyq%itU05;7S^~#$&q2>CptWSME^r;+ znEh+E76>)g?kV>@RW?=p^MRXz+UApwVo#@aj;W2sIT1A*QjIXRVX9e4n?GVlUC3on4O z@&YFvQzx9N-LRDJ``L~ye`VPraCGkD>4N*g%U;x1g6!*#`uj(#Y%3Ot% zip9+1iw`40-%(wP{WyDM$!{33tii%9ZJKg! z>xZ-UKbEyy7`ZEKHIp|OR>^L81=ds^Eud{45~^}q3voejI zm-WM0`ybA@+7{$2IaL*&N`2lg! str: + text = (raw_text or "").upper() + if "INTERMEDIATE" in text: + return "intermediate" + return "matric" diff --git a/docling_pk/parsers/cnic.py b/docling_pk/parsers/cnic.py new file mode 100644 index 0000000..a8282f5 --- /dev/null +++ b/docling_pk/parsers/cnic.py @@ -0,0 +1,20 @@ +import re + +from docling_pk.schema import FieldResult + + +CNIC_PATTERN = re.compile(r"\b\d{5}-\d{7}-\d\b") +GENDER_PATTERN = re.compile(r"\b(?:gender\s*[:\-]?\s*)?(male|female|m|f)\b", re.IGNORECASE) + + +def extract(raw_text: str) -> dict[str, FieldResult]: + cnic_match = CNIC_PATTERN.search(raw_text or "") + gender_match = GENDER_PATTERN.search(raw_text or "") + + cnic_value = cnic_match.group(0) if cnic_match else None + gender_value = gender_match.group(1).upper() if gender_match else None + + return { + "cnic_number": FieldResult(value=cnic_value, confidence=0.95 if cnic_value else 0.0), + "gender": FieldResult(value=gender_value, confidence=0.9 if gender_value else 0.0), + } diff --git a/docling_pk/parsers/intermediate.py b/docling_pk/parsers/intermediate.py new file mode 100644 index 0000000..4ee77f9 --- /dev/null +++ b/docling_pk/parsers/intermediate.py @@ -0,0 +1,2 @@ +def extract(raw_text: str) -> dict: + return {} diff --git a/docling_pk/parsers/matric.py b/docling_pk/parsers/matric.py new file mode 100644 index 0000000..4ee77f9 --- /dev/null +++ b/docling_pk/parsers/matric.py @@ -0,0 +1,2 @@ +def extract(raw_text: str) -> dict: + return {} diff --git a/docling_pk/preprocessor.py b/docling_pk/preprocessor.py new file mode 100644 index 0000000..bd983ce --- /dev/null +++ b/docling_pk/preprocessor.py @@ -0,0 +1,24 @@ +from pathlib import Path + +import cv2 + + +class TooBlurryError(ValueError): + """Raised when an image is too blurry for reliable extraction.""" + + +def preprocess(image_path: str): + path = Path(image_path) + if not path.exists(): + raise FileNotFoundError(f"Image path does not exist: {image_path}") + + image = cv2.imread(str(path)) + if image is None: + raise ValueError(f"Invalid image file: {image_path}") + + gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + sharpness = float(cv2.Laplacian(gray, cv2.CV_64F).var()) + if sharpness < 25.0: + raise TooBlurryError("Image is too blurry to process reliably") + + return image