documente_shared 0.1.200b1__tar.gz → 0.1.202__tar.gz

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 (132) hide show
  1. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/PKG-INFO +1 -1
  2. documente_shared-0.1.202/documente_shared/domain/entities/uif_registry.py +112 -0
  3. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/workspace_document.py +7 -1
  4. documente_shared-0.1.202/documente_shared/domain/helpers/values.py +77 -0
  5. documente_shared-0.1.202/documente_shared/domain/repositories/uif_registry.py +14 -0
  6. documente_shared-0.1.202/documente_shared/infrastructure/lambdas.py +15 -0
  7. documente_shared-0.1.202/documente_shared/infrastructure/repositories/http_uif_registry.py +50 -0
  8. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/pyproject.toml +1 -1
  9. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/uv.lock +25 -25
  10. documente_shared-0.1.200b1/documente_shared/domain/helpers/values.py +0 -13
  11. documente_shared-0.1.200b1/documente_shared/infrastructure/lambdas.py +0 -38
  12. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/.claude/commands/commit.md +0 -0
  13. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/.github/workflows/publish.yml +0 -0
  14. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/.gitignore +0 -0
  15. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/.vscode/settings.json +0 -0
  16. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/Dockerfile +0 -0
  17. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/Makefile +0 -0
  18. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/README.md +0 -0
  19. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/__init__.py +0 -0
  20. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/cases.py +0 -0
  21. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/docker-compose.yml +0 -0
  22. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/__init__.py +0 -0
  23. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/__init__.py +0 -0
  24. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/dates.py +0 -0
  25. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/digest.py +0 -0
  26. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/exceptions.py +0 -0
  27. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/files.py +0 -0
  28. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/json.py +0 -0
  29. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/logging.py +0 -0
  30. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/numbers.py +0 -0
  31. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/payloads.py +0 -0
  32. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/query_params.py +0 -0
  33. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/retry_utils.py +0 -0
  34. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/time_utils.py +0 -0
  35. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/application/timezone.py +0 -0
  36. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/__init__.py +0 -0
  37. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/base_enum.py +0 -0
  38. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/constants.py +0 -0
  39. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/__init__.py +0 -0
  40. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/document.py +0 -0
  41. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/document_metadata.py +0 -0
  42. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/document_type.py +0 -0
  43. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/in_memory_document.py +0 -0
  44. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/layout.py +0 -0
  45. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/processing_case.py +0 -0
  46. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/processing_case_filters.py +0 -0
  47. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/processing_case_item.py +0 -0
  48. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/processing_case_item_filters.py +0 -0
  49. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/processing_documents.py +0 -0
  50. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/processing_event.py +0 -0
  51. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/processing_record.py +0 -0
  52. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/scaling.py +0 -0
  53. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/workspace.py +0 -0
  54. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/entities/workspace_document_page.py +0 -0
  55. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/enums/__init__.py +0 -0
  56. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/enums/circular_oficio.py +0 -0
  57. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/enums/common.py +0 -0
  58. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/enums/document.py +0 -0
  59. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/enums/document_type_record.py +0 -0
  60. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/enums/processing_case.py +0 -0
  61. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/enums/processing_case_validator.py +0 -0
  62. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/enums/vnext.py +0 -0
  63. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/enums/workspace.py +0 -0
  64. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/exceptions.py +0 -0
  65. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/filters/__init__.py +0 -0
  66. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/filters/document_type.py +0 -0
  67. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/filters/workspace.py +0 -0
  68. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/filters/workspace_document.py +0 -0
  69. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/filters/workspace_document_page.py +0 -0
  70. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/helpers/__init__.py +0 -0
  71. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/helpers/dicts.py +0 -0
  72. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/helpers/encoding.py +0 -0
  73. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/helpers/lists.py +0 -0
  74. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/interfaces/__init__.py +0 -0
  75. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/interfaces/queue.py +0 -0
  76. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/interfaces/scaling.py +0 -0
  77. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/pagination/__init__.py +0 -0
  78. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/pagination/entities.py +0 -0
  79. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/pagination/response.py +0 -0
  80. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/repositories/__init__.py +0 -0
  81. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/repositories/document.py +0 -0
  82. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/repositories/document_type.py +0 -0
  83. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/repositories/processing_case.py +0 -0
  84. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/repositories/processing_case_item.py +0 -0
  85. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/repositories/processing_record.py +0 -0
  86. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/repositories/workspace.py +0 -0
  87. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/repositories/workspace_document.py +0 -0
  88. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/domain/repositories/workspace_document_page.py +0 -0
  89. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/__init__.py +0 -0
  90. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/batch_queue.py +0 -0
  91. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/documente_client.py +0 -0
  92. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/dynamo_table.py +0 -0
  93. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/__init__.py +0 -0
  94. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/dynamo_document.py +0 -0
  95. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/dynamo_processing_case.py +0 -0
  96. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/dynamo_processing_case_item.py +0 -0
  97. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/http_document.py +0 -0
  98. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/http_document_type.py +0 -0
  99. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/http_processing_case.py +0 -0
  100. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/http_processing_case_item.py +0 -0
  101. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/http_processing_record.py +0 -0
  102. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/http_workspace.py +0 -0
  103. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/http_workspace_document.py +0 -0
  104. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/http_workspace_document_page.py +0 -0
  105. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/mem_document.py +0 -0
  106. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/mem_processing_case.py +0 -0
  107. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/mem_processing_case_item.py +0 -0
  108. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/repositories/mem_processing_record.py +0 -0
  109. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/s3_bucket.py +0 -0
  110. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/services/__init__.py +0 -0
  111. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/services/http_scaling.py +0 -0
  112. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/infrastructure/sqs_queue.py +0 -0
  113. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/presentation/__init__.py +0 -0
  114. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/documente_shared/presentation/presenters.py +0 -0
  115. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/publish.sh +0 -0
  116. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/__init__.py +0 -0
  117. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/__init__.py +0 -0
  118. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/application/__init__.py +0 -0
  119. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/application/test_files.py +0 -0
  120. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/application/test_payloads.py +0 -0
  121. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/conftest.py +0 -0
  122. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/domain/__init__.py +0 -0
  123. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/domain/entities/__init__.py +0 -0
  124. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/domain/entities/test_in_memory_document.py +0 -0
  125. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/domain/entities/test_processing_case.py +0 -0
  126. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/domain/entities/test_processing_case_item.py +0 -0
  127. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/domain/entities/test_processing_record.py +0 -0
  128. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/domain/enums/test_processing_case_validator.py +0 -0
  129. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/infrastructure/__init__.py +0 -0
  130. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/infrastructure/repositories/__init__.py +0 -0
  131. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documente_shared/infrastructure/repositories/test_http_processing_record.py +0 -0
  132. {documente_shared-0.1.200b1 → documente_shared-0.1.202}/tests/documents.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: documente_shared
3
- Version: 0.1.200b1
3
+ Version: 0.1.202
4
4
  Summary: Shared utilities for LlamitAI projects
5
5
  Author-email: Tech <tech@llamitai.com>
6
6
  License: MIT
@@ -0,0 +1,112 @@
1
+ import json
2
+ from dataclasses import dataclass, field
3
+ from datetime import date, datetime
4
+ from decimal import Decimal
5
+ from uuid import UUID, uuid4
6
+
7
+ from documente_shared.application.digest import get_file_digest
8
+ from documente_shared.application.payloads import camel_to_snake, snake_to_camel
9
+ from documente_shared.domain.helpers.values import (
10
+ optional_date,
11
+ optional_datetime,
12
+ optional_decimal,
13
+ optional_text,
14
+ optional_uuid,
15
+ )
16
+
17
+ INSTANCE_FIELDS = frozenset({"uuid", "created_at"})
18
+
19
+
20
+ @dataclass
21
+ class UIFRegistry:
22
+ uuid: UUID = field(default_factory=uuid4)
23
+ paternal_last_name: str | None = None
24
+ maternal_last_name: str | None = None
25
+ first_name: str | None = None
26
+ identity_document_number: str | None = None
27
+ identity_document_extension: str | None = None
28
+ court_name: str | None = None
29
+ process_type: str | None = None
30
+ circular_id: str | None = None
31
+ circular_issue_date: date | None = None
32
+ involved_amount: Decimal | None = None
33
+ relation_type: str | None = None
34
+ due_diligence: bool | None = None
35
+ illicit_gains_legitimation: bool | None = None
36
+ document_digest: str | None = None
37
+ created_at: datetime | None = None
38
+
39
+ @property
40
+ def full_name(self) -> str:
41
+ parts = [self.paternal_last_name, self.maternal_last_name, self.first_name]
42
+ return " ".join(part for part in parts if part)
43
+
44
+ @property
45
+ def is_identifiable(self) -> bool:
46
+ return bool(self.full_name)
47
+
48
+ @property
49
+ def business_fields(self) -> dict:
50
+ return {
51
+ field_name: value
52
+ for field_name, value in self.to_dict.items()
53
+ if field_name not in INSTANCE_FIELDS
54
+ }
55
+
56
+ @property
57
+ def digest(self) -> str:
58
+ return get_file_digest(json.dumps(self.business_fields, sort_keys=True).encode())
59
+
60
+ @property
61
+ def to_dict(self) -> dict:
62
+ return {
63
+ "uuid": str(self.uuid),
64
+ "document_digest": self.document_digest,
65
+ "paternal_last_name": self.paternal_last_name,
66
+ "maternal_last_name": self.maternal_last_name,
67
+ "first_name": self.first_name,
68
+ "full_name": self.full_name,
69
+ "identity_document_number": self.identity_document_number,
70
+ "identity_document_extension": self.identity_document_extension,
71
+ "court_name": self.court_name,
72
+ "process_type": self.process_type,
73
+ "circular_id": self.circular_id,
74
+ "circular_issue_date": (
75
+ self.circular_issue_date.isoformat() if self.circular_issue_date else None
76
+ ),
77
+ "involved_amount": (
78
+ str(self.involved_amount) if self.involved_amount is not None else None
79
+ ),
80
+ "relation_type": self.relation_type,
81
+ "due_diligence": self.due_diligence,
82
+ "illicit_gains_legitimation": self.illicit_gains_legitimation,
83
+ "created_at": self.created_at.isoformat() if self.created_at else None,
84
+ }
85
+
86
+ @property
87
+ def to_payload(self) -> dict:
88
+ return snake_to_camel(self.to_dict)
89
+
90
+ @classmethod
91
+ def from_dict(cls, data: dict) -> "UIFRegistry":
92
+ normalized = camel_to_snake(data)
93
+ return cls(
94
+ uuid=optional_uuid(normalized.get("uuid")) or uuid4(),
95
+ document_digest=optional_text(normalized.get("document_digest")),
96
+ paternal_last_name=optional_text(normalized.get("paternal_last_name")),
97
+ maternal_last_name=optional_text(normalized.get("maternal_last_name")),
98
+ first_name=optional_text(normalized.get("first_name")),
99
+ identity_document_number=optional_text(normalized.get("identity_document_number")),
100
+ identity_document_extension=optional_text(
101
+ normalized.get("identity_document_extension")
102
+ ),
103
+ court_name=optional_text(normalized.get("court_name")),
104
+ process_type=optional_text(normalized.get("process_type")),
105
+ circular_id=optional_text(normalized.get("circular_id")),
106
+ circular_issue_date=optional_date(normalized.get("circular_issue_date")),
107
+ involved_amount=optional_decimal(normalized.get("involved_amount")),
108
+ relation_type=optional_text(normalized.get("relation_type")),
109
+ due_diligence=normalized.get("due_diligence"),
110
+ illicit_gains_legitimation=normalized.get("illicit_gains_legitimation"),
111
+ created_at=optional_datetime(normalized.get("created_at")),
112
+ )
@@ -27,6 +27,7 @@ class WorkspaceDocument(BaseModel):
27
27
  processing_time: Decimal | None = Field(default=None)
28
28
  metadata: dict | None = Field(default_factory=dict)
29
29
  extraction: dict | None = Field(default_factory=dict)
30
+ mapped_output: dict | None = Field(default=None)
30
31
  uploaded_at: datetime | None = Field(default=None)
31
32
  created_at: datetime | None = Field(default=None)
32
33
  updated_at: datetime | None = Field(default=None)
@@ -36,7 +37,7 @@ class WorkspaceDocument(BaseModel):
36
37
 
37
38
  @property
38
39
  def to_persist_dict(self) -> dict:
39
- return {
40
+ data = {
40
41
  "tenant_id": self.tenant_id,
41
42
  "workspace_id": str(self.workspace_id),
42
43
  "document_type_id": optional_str(self.document_type_id),
@@ -55,6 +56,9 @@ class WorkspaceDocument(BaseModel):
55
56
  "metadata": self.metadata or {},
56
57
  "extraction": self.extraction or {},
57
58
  }
59
+ if self.mapped_output is not None:
60
+ data["mapped_output"] = self.mapped_output
61
+ return data
58
62
 
59
63
  @property
60
64
  def to_dict(self) -> dict:
@@ -74,6 +78,7 @@ class WorkspaceDocument(BaseModel):
74
78
  "processing_time": optional_str(self.processing_time),
75
79
  "metadata": self.metadata,
76
80
  "extraction": self.extraction,
81
+ "mapped_output": self.mapped_output,
77
82
  "uploaded_at": optional_datetime_str(self.uploaded_at),
78
83
  "started_at": optional_datetime_str(self.started_at),
79
84
  "completed_at": optional_datetime_str(self.completed_at),
@@ -101,6 +106,7 @@ class WorkspaceDocument(BaseModel):
101
106
  ),
102
107
  metadata=data.get("metadata", {}),
103
108
  extraction=data.get("extraction", {}),
109
+ mapped_output=data.get("mapped_output", {}),
104
110
  uploaded_at=get_datetime_from_data(input_datetime=data.get("uploaded_at")),
105
111
  created_at=get_datetime_from_data(input_datetime=data.get("created_at")),
106
112
  updated_at=get_datetime_from_data(input_datetime=data.get("updated_at")),
@@ -0,0 +1,77 @@
1
+ from contextlib import suppress
2
+ from datetime import date, datetime
3
+ from decimal import Decimal, InvalidOperation
4
+ from uuid import UUID
5
+
6
+
7
+ def optional_str(input_value: object | None = None) -> str | None:
8
+ if input_value is None:
9
+ return None
10
+ return str(input_value)
11
+
12
+
13
+ def optional_text(input_value: object | None = None) -> str | None:
14
+ if input_value is None:
15
+ return None
16
+ text = str(input_value).strip()
17
+ return text or None
18
+
19
+
20
+ def optional_datetime_str(input_datetime: datetime | None = None) -> str | None:
21
+ if input_datetime is None:
22
+ return None
23
+ return input_datetime.isoformat()
24
+
25
+
26
+ def optional_uuid(input_value: object | None = None) -> UUID | None:
27
+ if input_value is None or isinstance(input_value, UUID):
28
+ return input_value
29
+ try:
30
+ return UUID(str(input_value))
31
+ except ValueError:
32
+ return None
33
+
34
+
35
+ def _date_from_formats(raw_value: str, formats: tuple[str, ...]) -> date | None:
36
+ for date_format in formats:
37
+ with suppress(ValueError):
38
+ return datetime.strptime(raw_value, date_format).date() # noqa: DTZ007
39
+ return None
40
+
41
+
42
+ def optional_date(
43
+ input_value: object | None = None,
44
+ formats: tuple[str, ...] = (),
45
+ ) -> date | None:
46
+ if input_value is None:
47
+ return None
48
+ if isinstance(input_value, datetime):
49
+ return input_value.date()
50
+ if isinstance(input_value, date):
51
+ return input_value
52
+
53
+ raw_value = str(input_value).strip()
54
+ parsed_date = _date_from_formats(raw_value, formats)
55
+ if parsed_date:
56
+ return parsed_date
57
+ with suppress(ValueError):
58
+ return date.fromisoformat(raw_value)
59
+ return None
60
+
61
+
62
+ def optional_datetime(input_value: object | None = None) -> datetime | None:
63
+ if input_value is None or isinstance(input_value, datetime):
64
+ return input_value
65
+ try:
66
+ return datetime.fromisoformat(str(input_value))
67
+ except ValueError:
68
+ return None
69
+
70
+
71
+ def optional_decimal(input_value: object | None = None) -> Decimal | None:
72
+ if input_value is None or isinstance(input_value, Decimal):
73
+ return input_value
74
+ try:
75
+ return Decimal(str(input_value))
76
+ except InvalidOperation:
77
+ return None
@@ -0,0 +1,14 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from documente_shared.domain.entities.uif_registry import UIFRegistry
4
+
5
+
6
+ class UIFRegistryRepository(ABC):
7
+ @abstractmethod
8
+ def set_document_uif_registries(
9
+ self,
10
+ document_digest: str,
11
+ registries: list[UIFRegistry],
12
+ replace: bool = True,
13
+ ) -> list[UIFRegistry]:
14
+ raise NotImplementedError
@@ -0,0 +1,15 @@
1
+ import json
2
+ import boto3
3
+ from botocore.config import Config
4
+
5
+
6
+ def invoke_lambda(function_name: str, payload: dict, read_timeout: int = 60) -> dict | list | None:
7
+ client = boto3.client("lambda", config=Config(read_timeout=read_timeout))
8
+
9
+ response = client.invoke(
10
+ FunctionName=function_name,
11
+ InvocationType="RequestResponse",
12
+ Payload=json.dumps(payload),
13
+ )
14
+
15
+ return json.loads(response["Payload"].read().decode("utf-8"))
@@ -0,0 +1,50 @@
1
+ from dataclasses import dataclass
2
+ from typing import TYPE_CHECKING
3
+
4
+ import structlog
5
+
6
+ from documente_shared.domain.entities.uif_registry import UIFRegistry
7
+ from documente_shared.domain.repositories.uif_registry import UIFRegistryRepository
8
+ from documente_shared.infrastructure.documente_client import DocumenteClientMixin
9
+
10
+ if TYPE_CHECKING:
11
+ from requests import Response
12
+
13
+ logger = structlog.get_logger()
14
+
15
+
16
+ @dataclass
17
+ class HttpUIFRegistryRepository(
18
+ DocumenteClientMixin,
19
+ UIFRegistryRepository,
20
+ ):
21
+ def set_document_uif_registries(
22
+ self,
23
+ document_digest: str,
24
+ registries: list[UIFRegistry],
25
+ replace: bool = True,
26
+ ) -> list[UIFRegistry]:
27
+ payload = {
28
+ "document_digest": document_digest,
29
+ "registries": [registry.to_payload for registry in registries],
30
+ "replace": replace,
31
+ }
32
+ logger.info(
33
+ "[shared.uif_registry.persisting]",
34
+ document_digest=document_digest,
35
+ registries=len(registries),
36
+ replace=replace,
37
+ )
38
+ response: Response = self.session.post(
39
+ url=f"{self.api_url}/v1/documents/uif-registry/",
40
+ json=payload,
41
+ )
42
+ if not self._is_success(response):
43
+ logger.warning(
44
+ "[shared.uif_registry.persist_error]",
45
+ document_digest=document_digest,
46
+ status_code=response.status_code,
47
+ response=response.text,
48
+ )
49
+ return []
50
+ return self._parse_list(response, UIFRegistry.from_dict)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "documente_shared"
3
- version = "0.1.200b1"
3
+ version = "0.1.202"
4
4
  description = "Shared utilities for LlamitAI projects"
5
5
  authors = [{ name = "Tech", email = "tech@llamitai.com" }]
6
6
  license = { text = "MIT" }
@@ -154,7 +154,7 @@ wheels = [
154
154
 
155
155
  [[package]]
156
156
  name = "documente-shared"
157
- version = "0.1.200b1"
157
+ version = "0.1.202"
158
158
  source = { editable = "." }
159
159
  dependencies = [
160
160
  { name = "boto3" },
@@ -200,7 +200,7 @@ name = "exceptiongroup"
200
200
  version = "1.3.1"
201
201
  source = { registry = "https://pypi.org/simple" }
202
202
  dependencies = [
203
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
203
+ { name = "typing-extensions" },
204
204
  ]
205
205
  sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
206
206
  wheels = [
@@ -263,17 +263,17 @@ resolution-markers = [
263
263
  "python_full_version < '3.11'",
264
264
  ]
265
265
  dependencies = [
266
- { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" },
267
- { name = "decorator", marker = "python_full_version < '3.11'" },
268
- { name = "exceptiongroup", marker = "python_full_version < '3.11'" },
269
- { name = "jedi", marker = "python_full_version < '3.11'" },
270
- { name = "matplotlib-inline", marker = "python_full_version < '3.11'" },
271
- { name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
272
- { name = "prompt-toolkit", marker = "python_full_version < '3.11'" },
273
- { name = "pygments", marker = "python_full_version < '3.11'" },
274
- { name = "stack-data", marker = "python_full_version < '3.11'" },
275
- { name = "traitlets", marker = "python_full_version < '3.11'" },
276
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
266
+ { name = "colorama", marker = "sys_platform == 'win32'" },
267
+ { name = "decorator" },
268
+ { name = "exceptiongroup" },
269
+ { name = "jedi" },
270
+ { name = "matplotlib-inline" },
271
+ { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
272
+ { name = "prompt-toolkit" },
273
+ { name = "pygments" },
274
+ { name = "stack-data" },
275
+ { name = "traitlets" },
276
+ { name = "typing-extensions" },
277
277
  ]
278
278
  sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996, upload-time = "2026-01-05T10:59:06.901Z" }
279
279
  wheels = [
@@ -288,17 +288,17 @@ resolution-markers = [
288
288
  "python_full_version >= '3.11'",
289
289
  ]
290
290
  dependencies = [
291
- { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" },
292
- { name = "decorator", marker = "python_full_version >= '3.11'" },
293
- { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" },
294
- { name = "jedi", marker = "python_full_version >= '3.11'" },
295
- { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" },
296
- { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" },
297
- { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" },
298
- { name = "pygments", marker = "python_full_version >= '3.11'" },
299
- { name = "stack-data", marker = "python_full_version >= '3.11'" },
300
- { name = "traitlets", marker = "python_full_version >= '3.11'" },
301
- { name = "typing-extensions", marker = "python_full_version == '3.11.*'" },
291
+ { name = "colorama", marker = "sys_platform == 'win32'" },
292
+ { name = "decorator" },
293
+ { name = "ipython-pygments-lexers" },
294
+ { name = "jedi" },
295
+ { name = "matplotlib-inline" },
296
+ { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" },
297
+ { name = "prompt-toolkit" },
298
+ { name = "pygments" },
299
+ { name = "stack-data" },
300
+ { name = "traitlets" },
301
+ { name = "typing-extensions", marker = "python_full_version < '3.12'" },
302
302
  ]
303
303
  sdist = { url = "https://files.pythonhosted.org/packages/a6/60/2111715ea11f39b1535bed6024b7dec7918b71e5e5d30855a5b503056b50/ipython-9.10.0.tar.gz", hash = "sha256:cd9e656be97618a0676d058134cd44e6dc7012c0e5cb36a9ce96a8c904adaf77", size = 4426526, upload-time = "2026-02-02T10:00:33.594Z" }
304
304
  wheels = [
@@ -310,7 +310,7 @@ name = "ipython-pygments-lexers"
310
310
  version = "1.1.1"
311
311
  source = { registry = "https://pypi.org/simple" }
312
312
  dependencies = [
313
- { name = "pygments", marker = "python_full_version >= '3.11'" },
313
+ { name = "pygments" },
314
314
  ]
315
315
  sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" }
316
316
  wheels = [
@@ -1,13 +0,0 @@
1
- from datetime import datetime
2
-
3
-
4
- def optional_str(input_value: object | None = None) -> str | None:
5
- if input_value is None:
6
- return None
7
- return str(input_value)
8
-
9
-
10
- def optional_datetime_str(input_datetime: datetime | None = None) -> str | None:
11
- if input_datetime is None:
12
- return None
13
- return input_datetime.isoformat()
@@ -1,38 +0,0 @@
1
- import json
2
- from functools import lru_cache
3
-
4
- import boto3
5
- from botocore.config import Config
6
-
7
- DEFAULT_LAMBDA_READ_TIMEOUT = 960
8
-
9
-
10
- @lru_cache(maxsize=None)
11
- def _get_lambda_client(read_timeout: int):
12
- # Invocations are long running and not idempotent. The read timeout must exceed the target
13
- # function timeout (900s is Lambda's hard maximum) so the function always fails before the
14
- # client does: that keeps a read timeout from being retried into a duplicate invocation, and
15
- # leaves the single retry for connection level failures, where no invocation happened.
16
- lambda_config = Config(
17
- read_timeout=read_timeout,
18
- connect_timeout=30,
19
- tcp_keepalive=True,
20
- retries={"max_attempts": 1, "mode": "standard"},
21
- )
22
- return boto3.client("lambda", config=lambda_config)
23
-
24
-
25
- def invoke_lambda(
26
- function_name: str,
27
- payload: dict,
28
- read_timeout: int = DEFAULT_LAMBDA_READ_TIMEOUT,
29
- ) -> dict | list | None:
30
- client = _get_lambda_client(read_timeout)
31
-
32
- response = client.invoke(
33
- FunctionName=function_name,
34
- InvocationType="RequestResponse",
35
- Payload=json.dumps(payload),
36
- )
37
-
38
- return json.loads(response["Payload"].read().decode("utf-8"))