mindee-lite 5.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (418) hide show
  1. docs/conf.py +76 -0
  2. mindee/__init__.py +53 -0
  3. mindee/__main__.py +4 -0
  4. mindee/cli.py +7 -0
  5. mindee/client_mixin.py +23 -0
  6. mindee/client_options/__init__.py +0 -0
  7. mindee/client_options/polling_options.py +19 -0
  8. mindee/dependencies/__init__.py +9 -0
  9. mindee/dependencies/checkers.py +33 -0
  10. mindee/dependencies/decorators.py +30 -0
  11. mindee/error/__init__.py +27 -0
  12. mindee/error/geometry_error.py +2 -0
  13. mindee/error/mimetype_error.py +2 -0
  14. mindee/error/mindee_dependency_error.py +5 -0
  15. mindee/error/mindee_error.py +14 -0
  16. mindee/error/mindee_http_error.py +115 -0
  17. mindee/error/mindee_image_error.py +2 -0
  18. mindee/error/mindee_pdf_error.py +2 -0
  19. mindee/geometry/__init__.py +31 -0
  20. mindee/geometry/bbox.py +73 -0
  21. mindee/geometry/minmax.py +32 -0
  22. mindee/geometry/point.py +17 -0
  23. mindee/geometry/polygon.py +76 -0
  24. mindee/geometry/polygon_utils.py +36 -0
  25. mindee/geometry/quadrilateral.py +57 -0
  26. mindee/image/__init__.py +3 -0
  27. mindee/image/extracted_image.py +108 -0
  28. mindee/image/image_compressor.py +45 -0
  29. mindee/image/image_extractor.py +186 -0
  30. mindee/input/__init__.py +19 -0
  31. mindee/input/base_64_input.py +20 -0
  32. mindee/input/bytes_input.py +19 -0
  33. mindee/input/file_input.py +28 -0
  34. mindee/input/local_input_source.py +284 -0
  35. mindee/input/local_response.py +118 -0
  36. mindee/input/page_options.py +30 -0
  37. mindee/input/path_input.py +19 -0
  38. mindee/input/url_input_source.py +228 -0
  39. mindee/logger.py +9 -0
  40. mindee/mindee_http/__init__.py +11 -0
  41. mindee/mindee_http/response_validation.py +76 -0
  42. mindee/mindee_http/settings_mixin.py +15 -0
  43. mindee/parsing/__init__.py +5 -0
  44. mindee/parsing/common/__init__.py +15 -0
  45. mindee/parsing/common/common_response.py +29 -0
  46. mindee/parsing/common/string_dict.py +4 -0
  47. mindee/parsing/common/summary_helper.py +33 -0
  48. mindee/pdf/__init__.py +15 -0
  49. mindee/pdf/extracted_pdf.py +60 -0
  50. mindee/pdf/pdf_char_data.py +31 -0
  51. mindee/pdf/pdf_compressor.py +242 -0
  52. mindee/pdf/pdf_extractor.py +103 -0
  53. mindee/pdf/pdf_utils.py +300 -0
  54. mindee/py.typed +0 -0
  55. mindee/v1/__init__.py +17 -0
  56. mindee/v1/client.py +616 -0
  57. mindee/v1/client_options/__init__.py +4 -0
  58. mindee/v1/client_options/predict_options.py +28 -0
  59. mindee/v1/client_options/workflow_options.py +30 -0
  60. mindee/v1/commands/__init__.py +9 -0
  61. mindee/v1/commands/cli_parser.py +282 -0
  62. mindee/v1/commands/cli_products.py +109 -0
  63. mindee/v1/error/__init__.py +4 -0
  64. mindee/v1/error/mindee_api_error.py +5 -0
  65. mindee/v1/error/mindee_product_error.py +5 -0
  66. mindee/v1/mindee_http/__init__.py +14 -0
  67. mindee/v1/mindee_http/base_endpoint.py +24 -0
  68. mindee/v1/mindee_http/base_settings.py +63 -0
  69. mindee/v1/mindee_http/endpoint.py +388 -0
  70. mindee/v1/mindee_http/mindee_api.py +29 -0
  71. mindee/v1/mindee_http/workflow_endpoint.py +75 -0
  72. mindee/v1/mindee_http/workflow_settings.py +24 -0
  73. mindee/v1/parsing/__init__.py +8 -0
  74. mindee/v1/parsing/common/__init__.py +43 -0
  75. mindee/v1/parsing/common/api_request.py +28 -0
  76. mindee/v1/parsing/common/api_response.py +29 -0
  77. mindee/v1/parsing/common/async_predict_response.py +35 -0
  78. mindee/v1/parsing/common/document.py +84 -0
  79. mindee/v1/parsing/common/execution.py +84 -0
  80. mindee/v1/parsing/common/execution_file.py +15 -0
  81. mindee/v1/parsing/common/execution_priority.py +9 -0
  82. mindee/v1/parsing/common/extras/__init__.py +7 -0
  83. mindee/v1/parsing/common/extras/cropper_extra.py +19 -0
  84. mindee/v1/parsing/common/extras/extras.py +43 -0
  85. mindee/v1/parsing/common/extras/full_text_ocr_extra.py +18 -0
  86. mindee/v1/parsing/common/extras/rag_extra.py +11 -0
  87. mindee/v1/parsing/common/feedback_response.py +13 -0
  88. mindee/v1/parsing/common/inference.py +77 -0
  89. mindee/v1/parsing/common/job.py +50 -0
  90. mindee/v1/parsing/common/ocr/__init__.py +3 -0
  91. mindee/v1/parsing/common/ocr/mvision_v1.py +17 -0
  92. mindee/v1/parsing/common/ocr/ocr.py +15 -0
  93. mindee/v1/parsing/common/ocr/ocr_line.py +13 -0
  94. mindee/v1/parsing/common/ocr/ocr_page.py +75 -0
  95. mindee/v1/parsing/common/ocr/ocr_word.py +19 -0
  96. mindee/v1/parsing/common/orientation.py +38 -0
  97. mindee/v1/parsing/common/page.py +47 -0
  98. mindee/v1/parsing/common/predict_response.py +28 -0
  99. mindee/v1/parsing/common/prediction.py +18 -0
  100. mindee/v1/parsing/common/product.py +15 -0
  101. mindee/v1/parsing/common/workflow_response.py +21 -0
  102. mindee/v1/parsing/custom/__init__.py +11 -0
  103. mindee/v1/parsing/custom/classification.py +17 -0
  104. mindee/v1/parsing/custom/line_items.py +175 -0
  105. mindee/v1/parsing/custom/list.py +64 -0
  106. mindee/v1/parsing/generated/__init__.py +11 -0
  107. mindee/v1/parsing/generated/generated_list.py +54 -0
  108. mindee/v1/parsing/generated/generated_object.py +81 -0
  109. mindee/v1/parsing/standard/__init__.py +52 -0
  110. mindee/v1/parsing/standard/address.py +63 -0
  111. mindee/v1/parsing/standard/amount.py +44 -0
  112. mindee/v1/parsing/standard/base.py +197 -0
  113. mindee/v1/parsing/standard/boolean.py +33 -0
  114. mindee/v1/parsing/standard/classification.py +31 -0
  115. mindee/v1/parsing/standard/company_registration.py +45 -0
  116. mindee/v1/parsing/standard/date.py +54 -0
  117. mindee/v1/parsing/standard/locale.py +56 -0
  118. mindee/v1/parsing/standard/payment_details.py +93 -0
  119. mindee/v1/parsing/standard/position.py +78 -0
  120. mindee/v1/parsing/standard/tax.py +124 -0
  121. mindee/v1/parsing/standard/text.py +36 -0
  122. mindee/v1/pdf/__init__.py +4 -0
  123. mindee/v1/pdf/multi_receipts_extractor.py +33 -0
  124. mindee/v1/pdf/pdf_extractor.py +52 -0
  125. mindee/v1/product/__init__.py +96 -0
  126. mindee/v1/product/barcode_reader/__init__.py +9 -0
  127. mindee/v1/product/barcode_reader/barcode_reader_v1.py +37 -0
  128. mindee/v1/product/barcode_reader/barcode_reader_v1_document.py +45 -0
  129. mindee/v1/product/cropper/__init__.py +13 -0
  130. mindee/v1/product/cropper/cropper_v1.py +40 -0
  131. mindee/v1/product/cropper/cropper_v1_document.py +8 -0
  132. mindee/v1/product/cropper/cropper_v1_page.py +38 -0
  133. mindee/v1/product/custom/__init__.py +11 -0
  134. mindee/v1/product/custom/custom_v1.py +30 -0
  135. mindee/v1/product/custom/custom_v1_document.py +59 -0
  136. mindee/v1/product/custom/custom_v1_page.py +49 -0
  137. mindee/v1/product/financial_document/__init__.py +15 -0
  138. mindee/v1/product/financial_document/financial_document_v1.py +37 -0
  139. mindee/v1/product/financial_document/financial_document_v1_document.py +318 -0
  140. mindee/v1/product/financial_document/financial_document_v1_line_item.py +105 -0
  141. mindee/v1/product/fr/__init__.py +49 -0
  142. mindee/v1/product/fr/bank_account_details/__init__.py +23 -0
  143. mindee/v1/product/fr/bank_account_details/bank_account_details_v1.py +37 -0
  144. mindee/v1/product/fr/bank_account_details/bank_account_details_v1_document.py +46 -0
  145. mindee/v1/product/fr/bank_account_details/bank_account_details_v2.py +37 -0
  146. mindee/v1/product/fr/bank_account_details/bank_account_details_v2_bban.py +63 -0
  147. mindee/v1/product/fr/bank_account_details/bank_account_details_v2_document.py +56 -0
  148. mindee/v1/product/fr/carte_grise/__init__.py +9 -0
  149. mindee/v1/product/fr/carte_grise/carte_grise_v1.py +37 -0
  150. mindee/v1/product/fr/carte_grise/carte_grise_v1_document.py +313 -0
  151. mindee/v1/product/fr/id_card/__init__.py +23 -0
  152. mindee/v1/product/fr/id_card/id_card_v1.py +40 -0
  153. mindee/v1/product/fr/id_card/id_card_v1_document.py +99 -0
  154. mindee/v1/product/fr/id_card/id_card_v1_page.py +35 -0
  155. mindee/v1/product/fr/id_card/id_card_v2.py +40 -0
  156. mindee/v1/product/fr/id_card/id_card_v2_document.py +134 -0
  157. mindee/v1/product/fr/id_card/id_card_v2_page.py +42 -0
  158. mindee/v1/product/generated/__init__.py +11 -0
  159. mindee/v1/product/generated/generated_v1.py +31 -0
  160. mindee/v1/product/generated/generated_v1_document.py +40 -0
  161. mindee/v1/product/generated/generated_v1_page.py +42 -0
  162. mindee/v1/product/generated/generated_v1_prediction.py +78 -0
  163. mindee/v1/product/international_id/__init__.py +9 -0
  164. mindee/v1/product/international_id/international_id_v2.py +37 -0
  165. mindee/v1/product/international_id/international_id_v2_document.py +152 -0
  166. mindee/v1/product/invoice/__init__.py +13 -0
  167. mindee/v1/product/invoice/invoice_v4.py +37 -0
  168. mindee/v1/product/invoice/invoice_v4_document.py +285 -0
  169. mindee/v1/product/invoice/invoice_v4_line_item.py +105 -0
  170. mindee/v1/product/invoice_splitter/__init__.py +13 -0
  171. mindee/v1/product/invoice_splitter/invoice_splitter_v1.py +37 -0
  172. mindee/v1/product/invoice_splitter/invoice_splitter_v1_document.py +55 -0
  173. mindee/v1/product/invoice_splitter/invoice_splitter_v1_invoice_page_group.py +51 -0
  174. mindee/v1/product/multi_receipts_detector/__init__.py +11 -0
  175. mindee/v1/product/multi_receipts_detector/multi_receipts_detector_v1.py +37 -0
  176. mindee/v1/product/multi_receipts_detector/multi_receipts_detector_v1_document.py +35 -0
  177. mindee/v1/product/passport/__init__.py +9 -0
  178. mindee/v1/product/passport/passport_v1.py +37 -0
  179. mindee/v1/product/passport/passport_v1_document.py +106 -0
  180. mindee/v1/product/receipt/__init__.py +13 -0
  181. mindee/v1/product/receipt/receipt_v5.py +37 -0
  182. mindee/v1/product/receipt/receipt_v5_document.py +180 -0
  183. mindee/v1/product/receipt/receipt_v5_line_item.py +77 -0
  184. mindee/v1/product/us/__init__.py +13 -0
  185. mindee/v1/product/us/bank_check/__init__.py +13 -0
  186. mindee/v1/product/us/bank_check/bank_check_v1.py +40 -0
  187. mindee/v1/product/us/bank_check/bank_check_v1_document.py +72 -0
  188. mindee/v1/product/us/bank_check/bank_check_v1_page.py +45 -0
  189. mindee/v2/__init__.py +40 -0
  190. mindee/v2/cli.py +11 -0
  191. mindee/v2/client.py +190 -0
  192. mindee/v2/client_options/__init__.py +0 -0
  193. mindee/v2/client_options/base_parameters.py +44 -0
  194. mindee/v2/commands/__init__.py +0 -0
  195. mindee/v2/commands/cli_parser.py +136 -0
  196. mindee/v2/error/__init__.py +4 -0
  197. mindee/v2/error/mindee_api_v2_error.py +5 -0
  198. mindee/v2/error/mindee_http_error_v2.py +51 -0
  199. mindee/v2/file_operations/__init__.py +12 -0
  200. mindee/v2/file_operations/crop.py +52 -0
  201. mindee/v2/file_operations/crop_files.py +20 -0
  202. mindee/v2/file_operations/split.py +38 -0
  203. mindee/v2/file_operations/split_files.py +20 -0
  204. mindee/v2/mindee_http/__init__.py +0 -0
  205. mindee/v2/mindee_http/mindee_api_v2.py +322 -0
  206. mindee/v2/mindee_http/response_validation_v2.py +35 -0
  207. mindee/v2/parsing/__init__.py +25 -0
  208. mindee/v2/parsing/error/__init__.py +4 -0
  209. mindee/v2/parsing/error/error_item.py +14 -0
  210. mindee/v2/parsing/error/error_response.py +30 -0
  211. mindee/v2/parsing/inference/__init__.py +0 -0
  212. mindee/v2/parsing/inference/base_inference.py +29 -0
  213. mindee/v2/parsing/inference/base_response.py +22 -0
  214. mindee/v2/parsing/inference/field/__init__.py +17 -0
  215. mindee/v2/parsing/inference/field/base_field.py +46 -0
  216. mindee/v2/parsing/inference/field/factory.py +17 -0
  217. mindee/v2/parsing/inference/field/field_confidence.py +36 -0
  218. mindee/v2/parsing/inference/field/field_location.py +28 -0
  219. mindee/v2/parsing/inference/field/inference_fields.py +63 -0
  220. mindee/v2/parsing/inference/field/list_field.py +58 -0
  221. mindee/v2/parsing/inference/field/object_field.py +133 -0
  222. mindee/v2/parsing/inference/field/simple_field.py +21 -0
  223. mindee/v2/parsing/inference/inference_active_options.py +64 -0
  224. mindee/v2/parsing/inference/inference_file.py +29 -0
  225. mindee/v2/parsing/inference/inference_job.py +14 -0
  226. mindee/v2/parsing/inference/inference_model.py +14 -0
  227. mindee/v2/parsing/inference/rag_metadata.py +10 -0
  228. mindee/v2/parsing/inference/raw_text.py +21 -0
  229. mindee/v2/parsing/inference/raw_text_page.py +11 -0
  230. mindee/v2/parsing/job/__init__.py +5 -0
  231. mindee/v2/parsing/job/job.py +55 -0
  232. mindee/v2/parsing/job/job_response.py +14 -0
  233. mindee/v2/parsing/job/job_webhook.py +35 -0
  234. mindee/v2/parsing/search/__init__.py +0 -0
  235. mindee/v2/parsing/search/model_webhook.py +17 -0
  236. mindee/v2/parsing/search/paginationmetadata.py +28 -0
  237. mindee/v2/parsing/search/search_model.py +25 -0
  238. mindee/v2/parsing/search/search_models.py +24 -0
  239. mindee/v2/parsing/search/search_response.py +32 -0
  240. mindee/v2/product/__init__.py +23 -0
  241. mindee/v2/product/classification/__init__.py +21 -0
  242. mindee/v2/product/classification/classification_classifier.py +22 -0
  243. mindee/v2/product/classification/classification_inference.py +27 -0
  244. mindee/v2/product/classification/classification_response.py +21 -0
  245. mindee/v2/product/classification/classification_result.py +16 -0
  246. mindee/v2/product/classification/params/__init__.py +5 -0
  247. mindee/v2/product/classification/params/classification_parameters.py +11 -0
  248. mindee/v2/product/crop/__init__.py +14 -0
  249. mindee/v2/product/crop/crop_inference.py +28 -0
  250. mindee/v2/product/crop/crop_item.py +43 -0
  251. mindee/v2/product/crop/crop_response.py +19 -0
  252. mindee/v2/product/crop/crop_result.py +29 -0
  253. mindee/v2/product/crop/params/__init__.py +3 -0
  254. mindee/v2/product/crop/params/crop_parameters.py +11 -0
  255. mindee/v2/product/extraction/__init__.py +21 -0
  256. mindee/v2/product/extraction/extraction_inference.py +28 -0
  257. mindee/v2/product/extraction/extraction_response.py +26 -0
  258. mindee/v2/product/extraction/extraction_result.py +27 -0
  259. mindee/v2/product/extraction/params/__init__.py +15 -0
  260. mindee/v2/product/extraction/params/data_schema.py +19 -0
  261. mindee/v2/product/extraction/params/data_schema_field.py +30 -0
  262. mindee/v2/product/extraction/params/data_schema_replace.py +20 -0
  263. mindee/v2/product/extraction/params/extraction_parameters.py +63 -0
  264. mindee/v2/product/extraction/params/string_data_class.py +17 -0
  265. mindee/v2/product/ocr/__init__.py +15 -0
  266. mindee/v2/product/ocr/ocr_inference.py +27 -0
  267. mindee/v2/product/ocr/ocr_page.py +23 -0
  268. mindee/v2/product/ocr/ocr_response.py +19 -0
  269. mindee/v2/product/ocr/ocr_result.py +19 -0
  270. mindee/v2/product/ocr/ocr_word.py +17 -0
  271. mindee/v2/product/ocr/params/__init__.py +3 -0
  272. mindee/v2/product/ocr/params/ocr_parameters.py +11 -0
  273. mindee/v2/product/split/__init__.py +13 -0
  274. mindee/v2/product/split/params/__init__.py +3 -0
  275. mindee/v2/product/split/params/split_parameters.py +11 -0
  276. mindee/v2/product/split/split_inference.py +28 -0
  277. mindee/v2/product/split/split_range.py +42 -0
  278. mindee/v2/product/split/split_response.py +19 -0
  279. mindee/v2/product/split/split_result.py +31 -0
  280. mindee/versions.py +21 -0
  281. mindee_lite-5.0.0.dist-info/METADATA +88 -0
  282. mindee_lite-5.0.0.dist-info/RECORD +418 -0
  283. mindee_lite-5.0.0.dist-info/WHEEL +5 -0
  284. mindee_lite-5.0.0.dist-info/entry_points.txt +3 -0
  285. mindee_lite-5.0.0.dist-info/licenses/LICENSE +21 -0
  286. mindee_lite-5.0.0.dist-info/top_level.txt +4 -0
  287. scripts/generate_lite_toml.py +42 -0
  288. tests/__init__.py +0 -0
  289. tests/conftest.py +16 -0
  290. tests/dependencies/__init__.py +0 -0
  291. tests/dependencies/test_dependencies.py +23 -0
  292. tests/input/__init__.py +0 -0
  293. tests/input/test_apply_page_options.py +164 -0
  294. tests/input/test_compression.py +232 -0
  295. tests/input/test_fix_pdf.py +24 -0
  296. tests/input/test_inputs.py +103 -0
  297. tests/test_geometry.py +137 -0
  298. tests/test_pkg_versions.py +34 -0
  299. tests/utils.py +55 -0
  300. tests/v1/__init__.py +0 -0
  301. tests/v1/api/__init__.py +0 -0
  302. tests/v1/api/test_async_response.py +121 -0
  303. tests/v1/api/test_feedback_response.py +14 -0
  304. tests/v1/api/test_response.py +86 -0
  305. tests/v1/extraction/__init__.py +0 -0
  306. tests/v1/extraction/test_image_extractor.py +46 -0
  307. tests/v1/extraction/test_invoice_splitter_auto_extraction.py +72 -0
  308. tests/v1/extraction/test_multi_receipts_extractor.py +121 -0
  309. tests/v1/extraction/test_pdf_extractor.py +85 -0
  310. tests/v1/extras/__init__.py +0 -0
  311. tests/v1/extras/test_extras_integration.py +31 -0
  312. tests/v1/extras/test_full_text_ocr.py +38 -0
  313. tests/v1/input/__init__.py +0 -0
  314. tests/v1/input/test_local_response.py +42 -0
  315. tests/v1/input/test_url_input_source_integration.py +65 -0
  316. tests/v1/mindee_http/__init__.py +0 -0
  317. tests/v1/mindee_http/test_error.py +116 -0
  318. tests/v1/parsing/__init__.py +0 -0
  319. tests/v1/parsing/common/__init__.py +0 -0
  320. tests/v1/parsing/common/test_ocr.py +14 -0
  321. tests/v1/parsing/common/test_orientation.py +25 -0
  322. tests/v1/parsing/standard/__init__.py +0 -0
  323. tests/v1/parsing/standard/test_amount.py +23 -0
  324. tests/v1/parsing/standard/test_date.py +27 -0
  325. tests/v1/parsing/standard/test_field.py +101 -0
  326. tests/v1/parsing/standard/test_locale.py +40 -0
  327. tests/v1/parsing/standard/test_payment_details.py +71 -0
  328. tests/v1/parsing/standard/test_position.py +36 -0
  329. tests/v1/parsing/standard/test_string.py +34 -0
  330. tests/v1/parsing/standard/test_tax.py +44 -0
  331. tests/v1/product/__init__.py +14 -0
  332. tests/v1/product/barcode_reader/__init__.py +0 -0
  333. tests/v1/product/barcode_reader/test_barcode_reader_v1.py +47 -0
  334. tests/v1/product/barcode_reader/test_barcode_reader_v1_regression.py +26 -0
  335. tests/v1/product/cropper/__init__.py +0 -0
  336. tests/v1/product/cropper/test_cropper_v1.py +66 -0
  337. tests/v1/product/cropper/test_cropper_v1_regression.py +26 -0
  338. tests/v1/product/custom/__init__.py +0 -0
  339. tests/v1/product/custom/test_custom_v1.py +124 -0
  340. tests/v1/product/custom/test_custom_v1_line_items.py +75 -0
  341. tests/v1/product/custom/test_custom_v1_v2.py +124 -0
  342. tests/v1/product/financial_document/__init__.py +0 -0
  343. tests/v1/product/financial_document/test_financial_document_v1.py +121 -0
  344. tests/v1/product/financial_document/test_financial_document_v1_regression.py +31 -0
  345. tests/v1/product/fr/__init__.py +0 -0
  346. tests/v1/product/fr/bank_account_details/__init__.py +0 -0
  347. tests/v1/product/fr/bank_account_details/test_bank_account_details_v1.py +50 -0
  348. tests/v1/product/fr/bank_account_details/test_bank_account_details_v1_regression.py +31 -0
  349. tests/v1/product/fr/bank_account_details/test_bank_account_details_v2.py +54 -0
  350. tests/v1/product/fr/bank_account_details/test_bank_account_details_v2_regression.py +31 -0
  351. tests/v1/product/fr/carte_grise/__init__.py +0 -0
  352. tests/v1/product/fr/carte_grise/test_carte_grise_v1.py +86 -0
  353. tests/v1/product/fr/carte_grise/test_carte_grise_v1_regression.py +26 -0
  354. tests/v1/product/fr/id_card/__init__.py +0 -0
  355. tests/v1/product/fr/id_card/test_id_card_v1.py +75 -0
  356. tests/v1/product/fr/id_card/test_id_card_v1_regression.py +26 -0
  357. tests/v1/product/fr/id_card/test_id_card_v2.py +80 -0
  358. tests/v1/product/fr/id_card/test_id_card_v2_regression.py +26 -0
  359. tests/v1/product/generated/__init__.py +0 -0
  360. tests/v1/product/generated/test_generated_v1.py +949 -0
  361. tests/v1/product/international_id/__init__.py +0 -0
  362. tests/v1/product/international_id/test_international_id_v2.py +61 -0
  363. tests/v1/product/invoice/__init__.py +0 -0
  364. tests/v1/product/invoice/test_invoice_v4.py +70 -0
  365. tests/v1/product/invoice/test_invoice_v4_regression.py +26 -0
  366. tests/v1/product/invoice_splitter/__init__.py +0 -0
  367. tests/v1/product/invoice_splitter/test_invoice_splitter_v1.py +46 -0
  368. tests/v1/product/invoice_splitter/test_invoice_splitter_v1_regression.py +25 -0
  369. tests/v1/product/multi_receipts_detector/__init__.py +0 -0
  370. tests/v1/product/multi_receipts_detector/test_multi_receipts_detector_v1.py +48 -0
  371. tests/v1/product/multi_receipts_detector/test_multi_receipts_detector_v1_regression.py +31 -0
  372. tests/v1/product/passport/__init__.py +0 -0
  373. tests/v1/product/passport/test_passport_v1.py +56 -0
  374. tests/v1/product/passport/test_passport_v1_regression.py +26 -0
  375. tests/v1/product/receipt/__init__.py +0 -0
  376. tests/v1/product/receipt/test_receipt_v5.py +59 -0
  377. tests/v1/product/receipt/test_receipt_v5_regression.py +26 -0
  378. tests/v1/product/us/__init__.py +0 -0
  379. tests/v1/product/us/bank_check/__init__.py +0 -0
  380. tests/v1/product/us/bank_check/test_bank_check_v1.py +71 -0
  381. tests/v1/product/us/bank_check/test_bank_check_v1_regression.py +26 -0
  382. tests/v1/test_cli.py +211 -0
  383. tests/v1/test_client.py +172 -0
  384. tests/v1/workflows/__init__.py +0 -0
  385. tests/v1/workflows/test_workflow.py +81 -0
  386. tests/v1/workflows/test_workflow_integration.py +111 -0
  387. tests/v2/__init__.py +0 -0
  388. tests/v2/file_operations/__init__.py +0 -0
  389. tests/v2/file_operations/test_crop_operation.py +71 -0
  390. tests/v2/file_operations/test_crop_operation_integration.py +67 -0
  391. tests/v2/file_operations/test_split_operation.py +57 -0
  392. tests/v2/file_operations/test_split_operation_integration.py +68 -0
  393. tests/v2/input/__init__.py +0 -0
  394. tests/v2/input/test_inference_parameters.py +85 -0
  395. tests/v2/input/test_local_response.py +51 -0
  396. tests/v2/parsing/__init__.py +0 -0
  397. tests/v2/parsing/test_job_response.py +61 -0
  398. tests/v2/product/__init__.py +0 -0
  399. tests/v2/product/classification/__init__.py +0 -0
  400. tests/v2/product/classification/test_classification_integration.py +36 -0
  401. tests/v2/product/classification/test_classification_response.py +50 -0
  402. tests/v2/product/crop/__init__.py +0 -0
  403. tests/v2/product/crop/test_crop_integration.py +32 -0
  404. tests/v2/product/crop/test_crop_response.py +97 -0
  405. tests/v2/product/extraction/__init__.py +0 -0
  406. tests/v2/product/extraction/test_extraction_response.py +341 -0
  407. tests/v2/product/ocr/__init__.py +0 -0
  408. tests/v2/product/ocr/test_ocr_integration.py +35 -0
  409. tests/v2/product/ocr/test_ocr_response.py +86 -0
  410. tests/v2/product/split/__init__.py +0 -0
  411. tests/v2/product/split/test_split_integration.py +32 -0
  412. tests/v2/product/split/test_split_response.py +80 -0
  413. tests/v2/product/utils.py +21 -0
  414. tests/v2/search/__init__.py +0 -0
  415. tests/v2/search/test_search_models.py +33 -0
  416. tests/v2/test_base_classes.py +49 -0
  417. tests/v2/test_client.py +340 -0
  418. tests/v2/test_client_integration.py +355 -0
docs/conf.py ADDED
@@ -0,0 +1,76 @@
1
+ # Configuration file for the Sphinx documentation builder.
2
+ #
3
+ # This file only contains a selection of the most common options. For a full
4
+ # list see the documentation:
5
+ # https://www.sphinx-doc.org/en/master/usage/configuration.html
6
+
7
+ # -- Path setup --------------------------------------------------------------
8
+
9
+ # If extensions (or modules to document with autodoc) are in another directory,
10
+ # add these directories to sys.path here. If the directory is relative to the
11
+ # documentation root, use os.path.abspath to make it absolute, like shown here.
12
+ #
13
+ # import os
14
+ # import sys
15
+ # sys.path.insert(0, os.path.abspath('.'))
16
+
17
+
18
+ # -- Project information -----------------------------------------------------
19
+
20
+ project = "Mindee Python Client"
21
+ copyright = "2022, Mindee"
22
+ author = "Mindee"
23
+
24
+
25
+ # -- General configuration ---------------------------------------------------
26
+
27
+ # Add any Sphinx extension module names here, as strings. They can be
28
+ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
29
+ # ones.
30
+ extensions = [
31
+ "sphinx.ext.autodoc",
32
+ "sphinx_autodoc_typehints",
33
+ ]
34
+
35
+ # Add any paths that contain templates here, relative to this directory.
36
+ templates_path = ["_templates"]
37
+
38
+ # List of patterns, relative to source directory, that match files and
39
+ # directories to ignore when looking for source files.
40
+ # This pattern also affects html_static_path and html_extra_path.
41
+ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
42
+
43
+
44
+ # -- Options for HTML output -------------------------------------------------
45
+
46
+ # The theme to use for HTML and HTML Help pages. See the documentation for
47
+ # a list of builtin themes.
48
+ #
49
+ html_theme = "sphinx_rtd_theme"
50
+ html_theme_options = {
51
+ "body_max_width": "none",
52
+ }
53
+
54
+ # Add any paths that contain custom static files (such as style sheets) here,
55
+ # relative to this directory. They are copied after the builtin static files,
56
+ # so a file named "default.css" will overwrite the builtin "default.css".
57
+ html_static_path = []
58
+
59
+ # A list of paths that contain extra files not directly related to the documentation.
60
+ html_extra_path = ["extras"]
61
+
62
+
63
+ # -- autodoc -----------------------------------------------------------------
64
+
65
+ autodoc_member_order = "groupwise"
66
+ # autodoc_class_signature = "separated" # breaks new syntaxes
67
+
68
+ # Hides prepended module names wich caused some classes to overflow on the right of the screen.
69
+ add_module_names = False
70
+ autodoc_typehints = "description"
71
+ autodoc_default_options = {"exclude-members": "__init__"}
72
+
73
+ # -- autodoc-typehints -------------------------------------------------------
74
+
75
+ typehints_defaults = "comma"
76
+ suppress_warnings = ["ref.python"]
mindee/__init__.py ADDED
@@ -0,0 +1,53 @@
1
+ from mindee.client_options.polling_options import PollingOptions
2
+ from mindee.input import PageOptions
3
+ from mindee.input.base_64_input import Base64Input
4
+ from mindee.input.bytes_input import BytesInput
5
+ from mindee.input.file_input import FileInput
6
+ from mindee.input.local_input_source import LocalInputSource
7
+ from mindee.input.local_response import LocalResponse
8
+ from mindee.input.path_input import PathInput
9
+ from mindee.input.url_input_source import URLInputSource
10
+ from mindee.v1 import product
11
+ from mindee.v2.parsing.job.job_response import JobResponse
12
+ from mindee.v2.product.classification.classification_response import (
13
+ ClassificationResponse,
14
+ )
15
+ from mindee.v2.product.classification.params.classification_parameters import (
16
+ ClassificationParameters,
17
+ )
18
+ from mindee.v2.product.crop.crop_response import CropResponse
19
+ from mindee.v2.product.crop.params.crop_parameters import CropParameters
20
+ from mindee.v2.product.extraction.extraction_response import ExtractionResponse
21
+ from mindee.v2.product.extraction.extraction_result import ExtractionResult
22
+ from mindee.v2.product.extraction.params.extraction_parameters import (
23
+ ExtractionParameters,
24
+ )
25
+ from mindee.v2.product.ocr.ocr_response import OCRResponse
26
+ from mindee.v2.product.ocr.params.ocr_parameters import OCRParameters
27
+ from mindee.v2.product.split.params.split_parameters import SplitParameters
28
+ from mindee.v2.product.split.split_response import SplitResponse
29
+
30
+ __all__ = [
31
+ "Base64Input",
32
+ "BytesInput",
33
+ "ClassificationParameters",
34
+ "ClassificationResponse",
35
+ "CropParameters",
36
+ "CropResponse",
37
+ "ExtractionParameters",
38
+ "ExtractionResponse",
39
+ "ExtractionResult",
40
+ "FileInput",
41
+ "JobResponse",
42
+ "LocalInputSource",
43
+ "LocalResponse",
44
+ "OCRParameters",
45
+ "OCRResponse",
46
+ "PageOptions",
47
+ "PathInput",
48
+ "PollingOptions",
49
+ "SplitParameters",
50
+ "SplitResponse",
51
+ "URLInputSource",
52
+ "product",
53
+ ]
mindee/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from mindee.cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
mindee/cli.py ADDED
@@ -0,0 +1,7 @@
1
+ from mindee.v1.commands.cli_parser import MindeeParser
2
+
3
+
4
+ def main() -> None:
5
+ """Run the Command Line Interface."""
6
+ parser = MindeeParser()
7
+ parser.call_parse()
mindee/client_mixin.py ADDED
@@ -0,0 +1,23 @@
1
+ from mindee.error import MindeeClientError
2
+
3
+
4
+ class ClientMixin:
5
+ """Mixin for clients V1 & V2 common static methods."""
6
+
7
+ @staticmethod
8
+ def _validate_async_params(
9
+ initial_delay_sec: float, delay_sec: float, max_retries: int
10
+ ) -> None:
11
+ min_delay = 1
12
+ min_initial_delay = 1
13
+ min_retries = 1
14
+ if delay_sec < min_delay:
15
+ raise MindeeClientError(
16
+ f"Cannot set auto-parsing delay to less than {min_delay} second(s)."
17
+ )
18
+ if initial_delay_sec < min_initial_delay:
19
+ raise MindeeClientError(
20
+ f"Cannot set initial parsing delay to less than {min_initial_delay} second(s)."
21
+ )
22
+ if max_retries < min_retries:
23
+ raise MindeeClientError(f"Cannot set retries to less than {min_retries}.")
File without changes
@@ -0,0 +1,19 @@
1
+ class PollingOptions:
2
+ """Options for asynchronous polling."""
3
+
4
+ initial_delay_sec: float
5
+ """Initial delay before the first polling attempt."""
6
+ delay_sec: float
7
+ """Delay between each polling attempt."""
8
+ max_retries: int
9
+ """Total number of polling attempts."""
10
+
11
+ def __init__(
12
+ self,
13
+ initial_delay_sec: float = 2,
14
+ delay_sec: float = 1.5,
15
+ max_retries: int = 80,
16
+ ):
17
+ self.initial_delay_sec = initial_delay_sec
18
+ self.delay_sec = delay_sec
19
+ self.max_retries = max_retries
@@ -0,0 +1,9 @@
1
+ from mindee.dependencies.checkers import PILLOW_AVAILABLE, PYPDFIUM2_AVAILABLE
2
+ from mindee.dependencies.decorators import requires_pillow, requires_pypdfium2
3
+
4
+ __all__ = [
5
+ "PILLOW_AVAILABLE",
6
+ "PYPDFIUM2_AVAILABLE",
7
+ "requires_pillow",
8
+ "requires_pypdfium2",
9
+ ]
@@ -0,0 +1,33 @@
1
+ from mindee.error.mindee_dependency_error import MindeeDependencyError
2
+
3
+ try:
4
+ import PIL # noqa: F401 #pylint: disable=unused-import
5
+
6
+ PILLOW_AVAILABLE = True
7
+ except ImportError:
8
+ PILLOW_AVAILABLE = False
9
+
10
+ try:
11
+ import pypdfium2 # noqa: F401 #pylint: disable=unused-import
12
+
13
+ PYPDFIUM2_AVAILABLE = True
14
+ except ImportError:
15
+ PYPDFIUM2_AVAILABLE = False
16
+
17
+
18
+ def require_pillow() -> None:
19
+ """Raises a clear error if Pillow is not installed."""
20
+ if not PILLOW_AVAILABLE:
21
+ raise MindeeDependencyError(
22
+ "This feature requires the 'Pillow' library. "
23
+ "Install it directly or run `pip install mindee` instead of `mindee-lite`."
24
+ )
25
+
26
+
27
+ def require_pypdfium2() -> None:
28
+ """Raises a clear error if PyPDFium2 is not installed."""
29
+ if not PYPDFIUM2_AVAILABLE:
30
+ raise MindeeDependencyError(
31
+ "This feature requires the 'PyPDFium2' library. "
32
+ "Install it directly or run `pip install mindee` instead of `mindee-lite`."
33
+ )
@@ -0,0 +1,30 @@
1
+ import functools
2
+ from collections.abc import Callable
3
+ from typing import ParamSpec, TypeVar
4
+
5
+ from mindee.dependencies.checkers import require_pillow, require_pypdfium2
6
+
7
+ P = ParamSpec("P")
8
+ R = TypeVar("R")
9
+
10
+
11
+ def requires_pillow(func: Callable[P, R]) -> Callable[P, R]:
12
+ """Decorator to enforce Pillow availability on a function/method."""
13
+
14
+ @functools.wraps(func)
15
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
16
+ require_pillow()
17
+ return func(*args, **kwargs)
18
+
19
+ return wrapper
20
+
21
+
22
+ def requires_pypdfium2(func: Callable[P, R]) -> Callable[P, R]:
23
+ """Decorator to enforce PyPDFium2 availability on a function/method."""
24
+
25
+ @functools.wraps(func)
26
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
27
+ require_pypdfium2()
28
+ return func(*args, **kwargs)
29
+
30
+ return wrapper
@@ -0,0 +1,27 @@
1
+ from mindee.error.geometry_error import MindeeGeometryError
2
+ from mindee.error.mimetype_error import MimeTypeError
3
+ from mindee.error.mindee_error import (
4
+ MindeeClientError,
5
+ MindeeError,
6
+ )
7
+ from mindee.error.mindee_http_error import (
8
+ MindeeHTTPClientError,
9
+ MindeeHTTPError,
10
+ MindeeHTTPServerError,
11
+ handle_error,
12
+ )
13
+ from mindee.error.mindee_image_error import MindeeImageError
14
+ from mindee.error.mindee_pdf_error import MindeePDFError
15
+
16
+ __all__ = [
17
+ "MimeTypeError",
18
+ "MindeeClientError",
19
+ "MindeeError",
20
+ "MindeeGeometryError",
21
+ "MindeeHTTPClientError",
22
+ "MindeeHTTPError",
23
+ "MindeeHTTPServerError",
24
+ "MindeeImageError",
25
+ "MindeePDFError",
26
+ "handle_error",
27
+ ]
@@ -0,0 +1,2 @@
1
+ class MindeeGeometryError(RuntimeError):
2
+ """An error related to geometry operations."""
@@ -0,0 +1,2 @@
1
+ class MimeTypeError(AssertionError):
2
+ """The MIME Type is not valid."""
@@ -0,0 +1,5 @@
1
+ from mindee.error import MindeeError
2
+
3
+
4
+ class MindeeDependencyError(MindeeError, ImportError):
5
+ """An exception relating to missing dependencies."""
@@ -0,0 +1,14 @@
1
+ class MindeeError(RuntimeError):
2
+ """A generic exception relating to various HTTP errors."""
3
+
4
+
5
+ class MindeeClientError(MindeeError):
6
+ """
7
+ An exception relating to document parsing errors.
8
+
9
+ Not to be confused with `MindeeHTTPClientError`.
10
+ """
11
+
12
+
13
+ class MindeeSourceError(MindeeError):
14
+ """An exception relating to document loading."""
@@ -0,0 +1,115 @@
1
+ from mindee.error.mindee_error import MindeeError
2
+ from mindee.parsing.common.string_dict import StringDict
3
+
4
+
5
+ class MindeeHTTPError(RuntimeError):
6
+ """An exception relating to HTTP calls."""
7
+
8
+ status_code: int
9
+ api_code: str | None
10
+ api_details: str | None
11
+ api_message: str | None
12
+
13
+ def __init__(self, http_error: StringDict, url: str, code: int) -> None:
14
+ """
15
+ Base exception for HTTP calls.
16
+
17
+ :param http_error: formatted & parsed error
18
+ :param url: url/endpoint the exception was raised on
19
+ :param code: HTTP code for the error
20
+ """
21
+ self.status_code = code
22
+ self.api_code = http_error.get("code", None) if "code" in http_error else None
23
+ self.api_details = (
24
+ http_error.get("details", None) if "details" in http_error else None
25
+ )
26
+ self.api_message = (
27
+ http_error.get("message", None) if "message" in http_error else None
28
+ )
29
+ super().__init__(
30
+ f"{url} {self.status_code} HTTP error: {self.api_details} - {self.api_message}"
31
+ )
32
+
33
+
34
+ def create_error_obj(response: StringDict | str) -> StringDict:
35
+ """
36
+ Creates an error object based on a requests' payload.
37
+
38
+ :param response: response as sent by the server, as a dict.
39
+ In _very_ rare instances, this can be an html string.
40
+ """
41
+ if not isinstance(response, str):
42
+ if "api_request" in response and "error" in response["api_request"]:
43
+ return response["api_request"]["error"]
44
+ raise MindeeError(f"Could not build specific HTTP exception from '{response}'")
45
+ error_dict = {}
46
+ if "Maximum pdf pages" in response:
47
+ error_dict = {
48
+ "code": "TooManyPages",
49
+ "message": "Maximum amound of pdf pages reached.",
50
+ "details": response,
51
+ }
52
+ elif "Max file size is" in response:
53
+ error_dict = {
54
+ "code": "FileTooLarge",
55
+ "message": "Maximum file size reached.",
56
+ "details": response,
57
+ }
58
+ elif "Invalid file type" in response:
59
+ error_dict = {
60
+ "code": "InvalidFiletype",
61
+ "message": "Invalid file type.",
62
+ "details": response,
63
+ }
64
+ elif "Gateway timeout" in response:
65
+ error_dict = {
66
+ "code": "RequestTimeout",
67
+ "message": "Request timed out.",
68
+ "details": response,
69
+ }
70
+ elif "Too Many Requests" in response:
71
+ error_dict = {
72
+ "code": "TooManyRequests",
73
+ "message": "Too Many Requests.",
74
+ "details": response,
75
+ }
76
+ else:
77
+ error_dict = {
78
+ "code": "UnknownError",
79
+ "message": "Server sent back an unexpected reply.",
80
+ "details": response,
81
+ }
82
+ return error_dict
83
+
84
+
85
+ class MindeeHTTPClientError(MindeeHTTPError):
86
+ """API Client HTTP exception."""
87
+
88
+
89
+ class MindeeHTTPServerError(MindeeHTTPError):
90
+ """API Server HTTP exception."""
91
+
92
+
93
+ def handle_error(url: str, response: StringDict) -> MindeeHTTPError:
94
+ """
95
+ Creates an appropriate HTTP error exception, based on retrieved HTTP error code.
96
+
97
+ :param url: url of the product
98
+ :param response: StringDict
99
+ """
100
+ error_obj = create_error_obj(response)
101
+ if not isinstance(response, str) and ( # type: ignore
102
+ "status_code" in response
103
+ and (
104
+ isinstance(response["status_code"], int)
105
+ or response["status_code"].isdigit()
106
+ )
107
+ ):
108
+ code = int(response["status_code"])
109
+ else:
110
+ code = 500
111
+ if 400 <= code <= 499:
112
+ return MindeeHTTPClientError(error_obj, url, code)
113
+ if 500 <= code <= 599:
114
+ return MindeeHTTPServerError(error_obj, url, code)
115
+ return MindeeHTTPError(error_obj, url, code)
@@ -0,0 +1,2 @@
1
+ class MindeeImageError(RuntimeError):
2
+ """An exception relating to errors during image operations."""
@@ -0,0 +1,2 @@
1
+ class MindeePDFError(RuntimeError):
2
+ """An exception relating to errors during PDF operations."""
@@ -0,0 +1,31 @@
1
+ from mindee.geometry.bbox import BBox, get_bbox
2
+ from mindee.geometry.minmax import MinMax, get_min_max_x, get_min_max_y
3
+ from mindee.geometry.point import Point, Points
4
+ from mindee.geometry.polygon import (
5
+ Polygon,
6
+ merge_polygons,
7
+ )
8
+ from mindee.geometry.polygon_utils import get_centroid, is_point_in_x, is_point_in_y
9
+ from mindee.geometry.quadrilateral import (
10
+ Quadrilateral,
11
+ get_bounding_box,
12
+ quadrilateral_from_prediction,
13
+ )
14
+
15
+ __all__ = [
16
+ "BBox",
17
+ "MinMax",
18
+ "Point",
19
+ "Points",
20
+ "Polygon",
21
+ "Quadrilateral",
22
+ "get_bbox",
23
+ "get_bounding_box",
24
+ "get_centroid",
25
+ "get_min_max_x",
26
+ "get_min_max_y",
27
+ "is_point_in_x",
28
+ "is_point_in_y",
29
+ "merge_polygons",
30
+ "quadrilateral_from_prediction",
31
+ ]
@@ -0,0 +1,73 @@
1
+ from typing import NamedTuple
2
+
3
+ from mindee.geometry.point import Points
4
+
5
+
6
+ class BBox(NamedTuple):
7
+ """Contains exactly 4 coordinates."""
8
+
9
+ x_min: float
10
+ """Minimum X coordinate."""
11
+ y_min: float
12
+ """Minimum Y coordinate."""
13
+ x_max: float
14
+ """Maximum X coordinate."""
15
+ y_max: float
16
+ """Maximum Y coordinate."""
17
+
18
+ @property
19
+ def width(self) -> float:
20
+ """The width of the BBox."""
21
+ return self.x_max - self.x_min
22
+
23
+ @property
24
+ def height(self) -> float:
25
+ """The height of the BBox."""
26
+ return self.y_max - self.y_min
27
+
28
+ @property
29
+ def area(self) -> float:
30
+ """The area of the BBox."""
31
+ return self.width * self.height
32
+
33
+
34
+ def get_bbox(points: Points) -> BBox:
35
+ """
36
+ Given a sequence of points, calculate a bbox that encompasses all points.
37
+
38
+ :param points: Polygon to process.
39
+ :return: A bbox that encompasses all points
40
+ """
41
+ y_min = min(v[1] for v in points)
42
+ y_max = max(v[1] for v in points)
43
+ x_min = min(v[0] for v in points)
44
+ x_max = max(v[0] for v in points)
45
+ return BBox(x_min, y_min, x_max, y_max)
46
+
47
+
48
+ def merge_bbox(bbox_1: BBox, bbox_2: BBox) -> BBox:
49
+ """Merges two BBox."""
50
+ return BBox(
51
+ min(bbox_1.x_min, bbox_2.x_min),
52
+ min(bbox_1.y_min, bbox_2.y_min),
53
+ max(bbox_1.x_max, bbox_2.x_max),
54
+ max(bbox_1.y_max, bbox_2.y_max),
55
+ )
56
+
57
+
58
+ def extend_bbox(bbox: BBox, points: Points) -> BBox:
59
+ """
60
+ Given a BBox and a sequence of points, calculate the surrounding bbox that encompasses all.
61
+
62
+ :param bbox: initial BBox to extend.
63
+ :param points: Sequence of points to process. Accepts polygons and similar
64
+ """
65
+ all_points = []
66
+ for point in points:
67
+ all_points.append(point)
68
+
69
+ y_min = min(v[1] for v in all_points)
70
+ y_max = max(v[1] for v in all_points)
71
+ x_min = min(v[0] for v in all_points)
72
+ x_max = max(v[0] for v in all_points)
73
+ return merge_bbox(bbox, BBox(x_min, y_min, x_max, y_max))
@@ -0,0 +1,32 @@
1
+ from typing import NamedTuple
2
+
3
+ from mindee.geometry.point import Points
4
+
5
+
6
+ class MinMax(NamedTuple):
7
+ """A set of minimum and maximum values."""
8
+
9
+ min: float
10
+ """Minimum"""
11
+ max: float
12
+ """Maximum"""
13
+
14
+
15
+ def get_min_max_y(points: Points) -> MinMax:
16
+ """
17
+ Get the maximum and minimum Y value given a sequence of points.
18
+
19
+ :param points: List of points
20
+ """
21
+ y_coords = [y for _, y in points]
22
+ return MinMax(min=min(y_coords), max=max(y_coords))
23
+
24
+
25
+ def get_min_max_x(points: Points) -> MinMax:
26
+ """
27
+ Get the maximum and minimum Y value given a sequence of points.
28
+
29
+ :param points: List of points
30
+ """
31
+ x_coords = [x for x, _ in points]
32
+ return MinMax(min=min(x_coords), max=max(x_coords))
@@ -0,0 +1,17 @@
1
+ from collections.abc import Sequence
2
+ from typing import NamedTuple
3
+
4
+
5
+ class Point(NamedTuple):
6
+ """A relative set of coordinates (X, Y) on the document."""
7
+
8
+ x: float
9
+ """X coordinate"""
10
+ y: float
11
+ """Y coordinate"""
12
+
13
+ def __str__(self) -> str:
14
+ return f"({self.x},{self.y})"
15
+
16
+
17
+ Points = Sequence[Point]
@@ -0,0 +1,76 @@
1
+ from collections.abc import Sequence
2
+
3
+ from mindee.error.geometry_error import MindeeGeometryError
4
+ from mindee.geometry.minmax import get_min_max_x, get_min_max_y
5
+ from mindee.geometry.point import Point
6
+ from mindee.geometry.polygon_utils import get_centroid, is_point_in_x, is_point_in_y
7
+
8
+
9
+ class Polygon(list[Point]):
10
+ """
11
+ Contains any number of vertex coordinates (Points).
12
+
13
+ Inherits from base class ``list`` so is compatible with type ``Points``.
14
+ """
15
+
16
+ def __init__(self, vertices: list | None = None):
17
+ # we should NOT allow the creation of invalid polygons, but it would be a breaking change
18
+ if not vertices:
19
+ vertices = []
20
+ else:
21
+ vertices = [Point(point[0], point[1]) for point in vertices]
22
+ super().__init__(vertices)
23
+
24
+ def _raise_if_invalid(self) -> None:
25
+ if len(self) < 3:
26
+ raise MindeeGeometryError("A polygon must have at least 3 vertices")
27
+
28
+ @property
29
+ def centroid(self) -> Point:
30
+ """The central point (centroid) of the polygon."""
31
+ self._raise_if_invalid()
32
+ return get_centroid(self)
33
+
34
+ def is_point_in_x(self, point: Point) -> bool:
35
+ """
36
+ Determine if the Point is in the Polygon's X-axis.
37
+
38
+ :param point: Point to compare
39
+ """
40
+ self._raise_if_invalid()
41
+ min_x, max_x = get_min_max_x(self)
42
+ return is_point_in_x(point, min_x, max_x)
43
+
44
+ def is_point_in_y(self, point: Point) -> bool:
45
+ """
46
+ Determine if the Point is in the Polygon's Y-axis.
47
+
48
+ :param point: Point to compare
49
+ """
50
+ self._raise_if_invalid()
51
+ min_y, max_y = get_min_max_y(self)
52
+ return is_point_in_y(point, min_y, max_y)
53
+
54
+ def __str__(self):
55
+ return "(" + ", ".join(str(p) for p in self) + ")"
56
+
57
+
58
+ def merge_polygons(vertices: Sequence[Polygon]) -> Polygon:
59
+ """
60
+ Given a sequence of polygons, calculate a polygon box that encompasses all polygons.
61
+
62
+ :param vertices: List of polygons
63
+ :return: A bounding box that encompasses all polygons
64
+ """
65
+ y_min = min(y for v in vertices for _, y in v)
66
+ y_max = max(y for v in vertices for _, y in v)
67
+ x_min = min(x for v in vertices for x, _ in v)
68
+ x_max = max(x for v in vertices for x, _ in v)
69
+ return Polygon(
70
+ [
71
+ Point(x_min, y_min),
72
+ Point(x_max, y_min),
73
+ Point(x_max, y_max),
74
+ Point(x_min, y_max),
75
+ ]
76
+ )