diff --git a/mindee/cli.py b/mindee/cli.py index 790c318c..7c528311 100644 --- a/mindee/cli.py +++ b/mindee/cli.py @@ -38,6 +38,10 @@ class CommandConfig(Generic[TypeDoc]): help="Financial Document (receipt or invoice)", doc_class=documents.TypeFinancialV1, ), + "proof-of-address": CommandConfig( + help="Proof of Address", + doc_class=documents.TypeProofOfAddressV1, + ), "us-check": CommandConfig( help="US Bank Check", doc_class=documents.us.TypeBankCheckV1, diff --git a/mindee/client.py b/mindee/client.py index 47a56f37..155f9c47 100644 --- a/mindee/client.py +++ b/mindee/client.py @@ -8,13 +8,14 @@ InvoiceV3, InvoiceV4, PassportV1, + ProofOfAddressV1, ReceiptV3, ReceiptV4, + fr, + us, ) from mindee.documents.base import Document, TypeDocument from mindee.documents.config import DocumentConfig, DocumentConfigDict -from mindee.documents.fr import CarteGriseV1 -from mindee.documents.us import BankCheckV1 from mindee.endpoints import OTS_OWNER, CustomEndpoint, HTTPException, StandardEndpoint from mindee.input.page_options import PageOptions from mindee.input.sources import ( @@ -249,24 +250,33 @@ def _init_default_endpoints(self) -> None: ) ], ), - (OTS_OWNER, BankCheckV1.__name__): DocumentConfig( + (OTS_OWNER, us.BankCheckV1.__name__): DocumentConfig( document_type="bank_check_v1", - document_class=BankCheckV1, + document_class=us.BankCheckV1, endpoints=[ StandardEndpoint( url_name="bank_check", version="1", api_key=self.api_key ) ], ), - (OTS_OWNER, CarteGriseV1.__name__): DocumentConfig( + (OTS_OWNER, fr.CarteGriseV1.__name__): DocumentConfig( document_type="carte_grise_v1", - document_class=CarteGriseV1, + document_class=fr.CarteGriseV1, endpoints=[ StandardEndpoint( url_name="carte_grise", version="1", api_key=self.api_key ) ], ), + (OTS_OWNER, ProofOfAddressV1.__name__): DocumentConfig( + document_type="proof_of_address_v1", + document_class=ProofOfAddressV1, + endpoints=[ + StandardEndpoint( + url_name="proof_of_address", version="1", api_key=self.api_key + ) + ], + ), (OTS_OWNER, CropperV1.__name__): DocumentConfig( document_type="cropper_v1", document_class=CropperV1, diff --git a/mindee/documents/__init__.py b/mindee/documents/__init__.py index 45846bf0..c3198617 100644 --- a/mindee/documents/__init__.py +++ b/mindee/documents/__init__.py @@ -4,4 +4,5 @@ from mindee.documents.financial import FinancialV1, TypeFinancialV1 from mindee.documents.invoice import InvoiceV3, InvoiceV4, TypeInvoiceV3, TypeInvoiceV4 from mindee.documents.passport import PassportV1, TypePassportV1 +from mindee.documents.proof_of_address import ProofOfAddressV1, TypeProofOfAddressV1 from mindee.documents.receipt import ReceiptV3, ReceiptV4, TypeReceiptV3, TypeReceiptV4 diff --git a/mindee/documents/proof_of_address/__init__.py b/mindee/documents/proof_of_address/__init__.py new file mode 100644 index 00000000..2d1e3184 --- /dev/null +++ b/mindee/documents/proof_of_address/__init__.py @@ -0,0 +1 @@ +from .proof_of_address_v1 import ProofOfAddressV1, TypeProofOfAddressV1 diff --git a/mindee/documents/proof_of_address/proof_of_address_v1.py b/mindee/documents/proof_of_address/proof_of_address_v1.py new file mode 100644 index 00000000..b25006e6 --- /dev/null +++ b/mindee/documents/proof_of_address/proof_of_address_v1.py @@ -0,0 +1,111 @@ +from typing import List, Optional, TypeVar + +from mindee.documents.base import Document, TypeApiPrediction, clean_out_string +from mindee.fields.company_registration import CompanyRegistrationField +from mindee.fields.date import DateField +from mindee.fields.locale import LocaleField +from mindee.fields.text import TextField + + +class ProofOfAddressV1(Document): + locale: LocaleField + """locale information""" + date: DateField + """ISO date yyyy-mm-dd. Works both for European and US dates.""" + dates: List[DateField] = [] + """All extracted ISO date yyyy-mm-dd""" + issuer_address: TextField + """Address of the document's issuer.""" + issuer_company_registration: List[CompanyRegistrationField] = [] + """Generic: VAT NUMBER, TAX ID, COMPANY REGISTRATION NUMBER or country specific.""" + issuer_name: TextField + """Name of the person or company issuing the document.""" + recipient_address: TextField + """Address of the recipient.""" + recipient_company_registration: List[CompanyRegistrationField] = [] + """Generic: VAT NUMBER, TAX ID, COMPANY REGISTRATION NUMBER or country specific.""" + recipient_name: TextField + """Name of the document's recipient.""" + + def __init__( + self, + api_prediction=None, + input_source=None, + page_n: Optional[int] = None, + document_type="proof_of_address", + ): + """ + Proof of Address document. + + :param api_prediction: Raw prediction from HTTP response + :param input_source: Input object + :param page_n: Page number for multi pages pdf input + """ + super().__init__( + input_source=input_source, + document_type=document_type, + api_prediction=api_prediction, + page_n=page_n, + ) + self._build_from_api_prediction(api_prediction["prediction"], page_n=page_n) + + def _build_from_api_prediction( + self, api_prediction: TypeApiPrediction, page_n: Optional[int] = None + ) -> None: + """ + Build the object from the prediction API JSON. + + :param api_prediction: Raw prediction from HTTP response + :param page_n: Page number for multi pages pdf input + """ + self.locale = LocaleField( + api_prediction["locale"], value_key="language", page_n=page_n + ) + self.date = DateField(api_prediction["date"], page_n=page_n) + self.dates = [ + DateField(tax_prediction, page_n=page_n) + for tax_prediction in api_prediction["dates"] + ] + self.issuer_name = TextField(api_prediction["issuer_name"], page_n=page_n) + self.issuer_address = TextField(api_prediction["issuer_address"], page_n=page_n) + self.issuer_company_registration = [ + CompanyRegistrationField(tax_prediction, page_n=page_n) + for tax_prediction in api_prediction["issuer_company_registration"] + ] + self.recipient_name = TextField(api_prediction["recipient_name"], page_n=page_n) + self.recipient_address = TextField( + api_prediction["recipient_address"], page_n=page_n + ) + self.recipient_company_registration = [ + CompanyRegistrationField(tax_prediction, page_n=page_n) + for tax_prediction in api_prediction["recipient_company_registration"] + ] + + def __str__(self) -> str: + issuer_company_registrations = "; ".join( + [str(n.value) for n in self.issuer_company_registration] + ) + recipient_company_registrations = "; ".join( + [str(n.value) for n in self.recipient_company_registration] + ) + dates = "\n ".join([str(n.value) for n in self.dates]) + return clean_out_string( + "----- Proof of Address V1 -----\n" + f"Filename: {self.filename or ''}\n" + f"Locale: {self.locale}\n" + f"Issuer name: {self.issuer_name}\n" + f"Issuer Address: {self.issuer_address}\n" + f"Issuer Company Registrations: {issuer_company_registrations}\n" + f"Recipient name: {self.recipient_name}\n" + f"Recipient Address: {self.recipient_address}\n" + f"Recipient Company Registrations: {recipient_company_registrations}\n" + f"Issuance Date: {self.date}\n" + f"Dates: {dates}\n" + "----------------------" + ) + + def _checklist(self) -> None: + pass + + +TypeProofOfAddressV1 = TypeVar("TypeProofOfAddressV1", bound=ProofOfAddressV1) diff --git a/tests/__init__.py b/tests/__init__.py index 577c3204..9c562f6e 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,6 +1,7 @@ RECEIPT_DATA_DIR = "./tests/data/receipt" INVOICE_DATA_DIR = "./tests/data/invoice" PASSPORT_DATA_DIR = "./tests/data/passport" +PROOF_OF_ADDRESS_DATA_DIR = "./tests/data/proof_of_address" US_BANK_CHECK_DATA_DIR = "./tests/data/us/bank_check" FR_CARTE_GRISE_DATA_DIR = "./tests/data/fr/carte_grise" CUSTOM_DATA_DIR = "./tests/data/custom" diff --git a/tests/data b/tests/data index e29d9e4f..3bcb6372 160000 --- a/tests/data +++ b/tests/data @@ -1 +1 @@ -Subproject commit e29d9e4f1b7253f6189599826e4387201a70316b +Subproject commit 3bcb6372262ab2fb090ed1a57b734cb080143878 diff --git a/tests/documents/test_proof_of_address_v1.py b/tests/documents/test_proof_of_address_v1.py new file mode 100644 index 00000000..b57824a4 --- /dev/null +++ b/tests/documents/test_proof_of_address_v1.py @@ -0,0 +1,68 @@ +import json + +import pytest + +from mindee.documents.proof_of_address.proof_of_address_v1 import ProofOfAddressV1 +from tests import PROOF_OF_ADDRESS_DATA_DIR + +FILE_PATH_PROOF_OF_ADDRESS_V1_COMPLETE = ( + f"{PROOF_OF_ADDRESS_DATA_DIR}/response_v1/complete.json" +) + + +@pytest.fixture +def proof_of_address_v1_doc_object() -> ProofOfAddressV1: + json_data = json.load(open(FILE_PATH_PROOF_OF_ADDRESS_V1_COMPLETE)) + return ProofOfAddressV1( + api_prediction=json_data["document"]["inference"], page_n=None + ) + + +@pytest.fixture +def proof_of_address_v1_doc_object_empty() -> ProofOfAddressV1: + json_data = json.load(open(f"{PROOF_OF_ADDRESS_DATA_DIR}/response_v1/empty.json")) + return ProofOfAddressV1( + api_prediction=json_data["document"]["inference"], page_n=None + ) + + +@pytest.fixture +def proof_of_address_v1_page_object() -> ProofOfAddressV1: + json_data = json.load(open(FILE_PATH_PROOF_OF_ADDRESS_V1_COMPLETE)) + return ProofOfAddressV1( + api_prediction=json_data["document"]["inference"]["pages"][0], page_n=0 + ) + + +def test_doc_constructor(proof_of_address_v1_doc_object): + doc_str = ( + open(f"{PROOF_OF_ADDRESS_DATA_DIR}/response_v1/doc_to_string.txt") + .read() + .strip() + ) + assert proof_of_address_v1_doc_object.issuer_name.page_n == 0 + assert str(proof_of_address_v1_doc_object) == doc_str + + +def test_page_constructor(proof_of_address_v1_page_object): + doc_str = ( + open(f"{PROOF_OF_ADDRESS_DATA_DIR}/response_v1/page0_to_string.txt") + .read() + .strip() + ) + assert proof_of_address_v1_page_object.orientation.value == 0 + assert proof_of_address_v1_page_object.issuer_name.page_n == 0 + assert str(proof_of_address_v1_page_object) == doc_str + assert len(proof_of_address_v1_page_object.cropper) == 0 + + +def test_all_na(proof_of_address_v1_doc_object_empty): + assert proof_of_address_v1_doc_object_empty.locale.value is None + assert proof_of_address_v1_doc_object_empty.date.value is None + assert len(proof_of_address_v1_doc_object_empty.dates) == 0 + assert proof_of_address_v1_doc_object_empty.issuer_address.value is None + assert len(proof_of_address_v1_doc_object_empty.issuer_company_registration) == 0 + assert proof_of_address_v1_doc_object_empty.issuer_name.value is None + assert proof_of_address_v1_doc_object_empty.recipient_address.value is None + assert len(proof_of_address_v1_doc_object_empty.recipient_company_registration) == 0 + assert proof_of_address_v1_doc_object_empty.recipient_name.value is None