documente_shared 0.1.159__tar.gz → 0.1.161__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 (111) hide show
  1. {documente_shared-0.1.159 → documente_shared-0.1.161}/PKG-INFO +1 -1
  2. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/processing_case.py +18 -12
  3. documente_shared-0.1.161/documente_shared/domain/entities/workspace.py +63 -0
  4. documente_shared-0.1.161/documente_shared/domain/entities/workspace_document.py +138 -0
  5. documente_shared-0.1.161/documente_shared/domain/entities/workspace_document_filters.py +45 -0
  6. documente_shared-0.1.161/documente_shared/domain/entities/workspace_document_page.py +70 -0
  7. documente_shared-0.1.161/documente_shared/domain/entities/workspace_document_page_filters.py +32 -0
  8. documente_shared-0.1.161/documente_shared/domain/entities/workspace_filters.py +35 -0
  9. documente_shared-0.1.161/documente_shared/domain/enums/workspace.py +19 -0
  10. documente_shared-0.1.161/documente_shared/domain/helpers/values.py +13 -0
  11. documente_shared-0.1.161/documente_shared/domain/repositories/workspace.py +28 -0
  12. documente_shared-0.1.161/documente_shared/domain/repositories/workspace_document.py +28 -0
  13. documente_shared-0.1.161/documente_shared/domain/repositories/workspace_document_page.py +28 -0
  14. documente_shared-0.1.161/documente_shared/infrastructure/repositories/http_workspace.py +71 -0
  15. documente_shared-0.1.161/documente_shared/infrastructure/repositories/http_workspace_document.py +71 -0
  16. documente_shared-0.1.161/documente_shared/infrastructure/repositories/http_workspace_document_page.py +71 -0
  17. {documente_shared-0.1.159 → documente_shared-0.1.161}/pyproject.toml +1 -1
  18. documente_shared-0.1.161/tests/documente_shared/infrastructure/__init__.py +0 -0
  19. {documente_shared-0.1.159 → documente_shared-0.1.161}/uv.lock +1 -1
  20. {documente_shared-0.1.159 → documente_shared-0.1.161}/.github/workflows/publish.yml +0 -0
  21. {documente_shared-0.1.159 → documente_shared-0.1.161}/.gitignore +0 -0
  22. {documente_shared-0.1.159 → documente_shared-0.1.161}/Dockerfile +0 -0
  23. {documente_shared-0.1.159 → documente_shared-0.1.161}/Makefile +0 -0
  24. {documente_shared-0.1.159 → documente_shared-0.1.161}/README.md +0 -0
  25. {documente_shared-0.1.159 → documente_shared-0.1.161}/__init__.py +0 -0
  26. {documente_shared-0.1.159 → documente_shared-0.1.161}/cases.py +0 -0
  27. {documente_shared-0.1.159 → documente_shared-0.1.161}/docker-compose.yml +0 -0
  28. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/__init__.py +0 -0
  29. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/__init__.py +0 -0
  30. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/dates.py +0 -0
  31. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/digest.py +0 -0
  32. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/exceptions.py +0 -0
  33. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/files.py +0 -0
  34. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/json.py +0 -0
  35. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/numbers.py +0 -0
  36. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/payloads.py +0 -0
  37. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/query_params.py +0 -0
  38. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/retry_utils.py +0 -0
  39. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/time_utils.py +0 -0
  40. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/application/timezone.py +0 -0
  41. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/__init__.py +0 -0
  42. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/base_enum.py +0 -0
  43. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/constants.py +0 -0
  44. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/__init__.py +0 -0
  45. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/document.py +0 -0
  46. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/document_metadata.py +0 -0
  47. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/in_memory_document.py +0 -0
  48. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/processing_case_filters.py +0 -0
  49. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/processing_case_item.py +0 -0
  50. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/processing_case_item_filters.py +0 -0
  51. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/processing_documents.py +0 -0
  52. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/processing_event.py +0 -0
  53. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/processing_record.py +0 -0
  54. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/entities/scaling.py +0 -0
  55. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/enums/__init__.py +0 -0
  56. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/enums/circular_oficio.py +0 -0
  57. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/enums/common.py +0 -0
  58. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/enums/document.py +0 -0
  59. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/enums/document_type_record.py +0 -0
  60. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/enums/processing_case.py +0 -0
  61. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/enums/processing_case_validator.py +0 -0
  62. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/exceptions.py +0 -0
  63. {documente_shared-0.1.159/documente_shared/domain/interfaces → documente_shared-0.1.161/documente_shared/domain/helpers}/__init__.py +0 -0
  64. {documente_shared-0.1.159/documente_shared/domain/repositories → documente_shared-0.1.161/documente_shared/domain/interfaces}/__init__.py +0 -0
  65. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/interfaces/queue.py +0 -0
  66. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/interfaces/scaling.py +0 -0
  67. {documente_shared-0.1.159/documente_shared/infrastructure → documente_shared-0.1.161/documente_shared/domain/repositories}/__init__.py +0 -0
  68. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/repositories/document.py +0 -0
  69. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/repositories/processing_case.py +0 -0
  70. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/repositories/processing_case_item.py +0 -0
  71. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/domain/repositories/processing_record.py +0 -0
  72. {documente_shared-0.1.159/documente_shared/infrastructure/repositories → documente_shared-0.1.161/documente_shared/infrastructure}/__init__.py +0 -0
  73. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/batch_queue.py +0 -0
  74. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/documente_client.py +0 -0
  75. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/dynamo_table.py +0 -0
  76. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/lambdas.py +0 -0
  77. {documente_shared-0.1.159/documente_shared/infrastructure/services → documente_shared-0.1.161/documente_shared/infrastructure/repositories}/__init__.py +0 -0
  78. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/repositories/dynamo_document.py +0 -0
  79. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/repositories/dynamo_processing_case.py +0 -0
  80. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/repositories/dynamo_processing_case_item.py +0 -0
  81. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/repositories/http_document.py +0 -0
  82. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/repositories/http_processing_case.py +0 -0
  83. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/repositories/http_processing_case_item.py +0 -0
  84. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/repositories/http_processing_record.py +0 -0
  85. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/repositories/mem_document.py +0 -0
  86. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/repositories/mem_processing_case.py +0 -0
  87. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/repositories/mem_processing_case_item.py +0 -0
  88. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/repositories/mem_processing_record.py +0 -0
  89. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/s3_bucket.py +0 -0
  90. {documente_shared-0.1.159/documente_shared/presentation → documente_shared-0.1.161/documente_shared/infrastructure/services}/__init__.py +0 -0
  91. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/services/http_scaling.py +0 -0
  92. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/infrastructure/sqs_queue.py +0 -0
  93. {documente_shared-0.1.159/tests → documente_shared-0.1.161/documente_shared/presentation}/__init__.py +0 -0
  94. {documente_shared-0.1.159 → documente_shared-0.1.161}/documente_shared/presentation/presenters.py +0 -0
  95. {documente_shared-0.1.159 → documente_shared-0.1.161}/publish.sh +0 -0
  96. {documente_shared-0.1.159/tests/documente_shared → documente_shared-0.1.161/tests}/__init__.py +0 -0
  97. {documente_shared-0.1.159/tests/documente_shared/application → documente_shared-0.1.161/tests/documente_shared}/__init__.py +0 -0
  98. {documente_shared-0.1.159/tests/documente_shared/domain → documente_shared-0.1.161/tests/documente_shared/application}/__init__.py +0 -0
  99. {documente_shared-0.1.159 → documente_shared-0.1.161}/tests/documente_shared/application/test_files.py +0 -0
  100. {documente_shared-0.1.159 → documente_shared-0.1.161}/tests/documente_shared/application/test_payloads.py +0 -0
  101. {documente_shared-0.1.159 → documente_shared-0.1.161}/tests/documente_shared/conftest.py +0 -0
  102. {documente_shared-0.1.159/tests/documente_shared/domain/entities → documente_shared-0.1.161/tests/documente_shared/domain}/__init__.py +0 -0
  103. {documente_shared-0.1.159/tests/documente_shared/infrastructure → documente_shared-0.1.161/tests/documente_shared/domain/entities}/__init__.py +0 -0
  104. {documente_shared-0.1.159 → documente_shared-0.1.161}/tests/documente_shared/domain/entities/test_in_memory_document.py +0 -0
  105. {documente_shared-0.1.159 → documente_shared-0.1.161}/tests/documente_shared/domain/entities/test_processing_case.py +0 -0
  106. {documente_shared-0.1.159 → documente_shared-0.1.161}/tests/documente_shared/domain/entities/test_processing_case_item.py +0 -0
  107. {documente_shared-0.1.159 → documente_shared-0.1.161}/tests/documente_shared/domain/entities/test_processing_record.py +0 -0
  108. {documente_shared-0.1.159 → documente_shared-0.1.161}/tests/documente_shared/domain/enums/test_processing_case_validator.py +0 -0
  109. {documente_shared-0.1.159 → documente_shared-0.1.161}/tests/documente_shared/infrastructure/repositories/__init__.py +0 -0
  110. {documente_shared-0.1.159 → documente_shared-0.1.161}/tests/documente_shared/infrastructure/repositories/test_http_processing_record.py +0 -0
  111. {documente_shared-0.1.159 → documente_shared-0.1.161}/tests/documents.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: documente_shared
3
- Version: 0.1.159
3
+ Version: 0.1.161
4
4
  Summary: Shared utilities for Documente AI projects
5
5
  Author-email: Tech <tech@llamitai.com>
6
6
  License: MIT
@@ -7,6 +7,7 @@ from documente_shared.domain.constants import la_paz_tz
7
7
  from documente_shared.domain.entities.processing_case_item import ProcessingCaseItem
8
8
  from documente_shared.domain.enums.common import ProcessingStatus
9
9
  from documente_shared.domain.enums.processing_case import ProcessingCaseType
10
+ from documente_shared.domain.helpers.values import optional_datetime_str
10
11
 
11
12
 
12
13
  @dataclass
@@ -21,6 +22,8 @@ class ProcessingCase(object):
21
22
  failed_at: Optional[datetime] = None
22
23
  feedback: Optional[list | dict] = None
23
24
  completed_at: Optional[datetime] = None
25
+ created_at: Optional[datetime] = None
26
+ updated_at: Optional[datetime] = None
24
27
  metadata: Optional[dict] = None
25
28
  items: Optional[List[ProcessingCaseItem]] = None
26
29
 
@@ -30,10 +33,10 @@ class ProcessingCase(object):
30
33
  def __eq__(self, other: 'ProcessingCase') -> bool:
31
34
  if not other:
32
35
  return False
33
-
34
36
  return (
35
37
  self.uuid == other.uuid
36
38
  and self.name == other.name
39
+ and self.tenant_slug == other.tenant_slug
37
40
  and self.status == other.status
38
41
  and self.case_type == other.case_type
39
42
  and self.enqueued_at == other.enqueued_at
@@ -41,16 +44,19 @@ class ProcessingCase(object):
41
44
  and self.failed_at == other.failed_at
42
45
  and self.feedback == other.feedback
43
46
  and self.completed_at == other.completed_at
47
+ and self.created_at == other.created_at
48
+ and self.updated_at == other.updated_at
44
49
  and self.metadata == other.metadata
50
+ and self.items == other.items
45
51
  )
46
52
 
47
53
  @property
48
- def strategy_id(self) ->str:
54
+ def strategy_id(self) -> str:
49
55
  return str(self.case_type)
50
56
 
51
57
  @property
52
58
  def is_procesable(self) -> bool:
53
- return self.items and len(self.items) > 0
59
+ return bool(self.items)
54
60
 
55
61
  @property
56
62
  def is_queue_procesable(self) -> bool:
@@ -86,18 +92,19 @@ class ProcessingCase(object):
86
92
  str(self.case_type)
87
93
  if self.case_type else None
88
94
  ),
89
- 'enqueued_at': self.enqueued_at.isoformat() if self.enqueued_at else None,
90
- 'started_at': self.started_at.isoformat() if self.started_at else None,
91
- 'failed_at': self.failed_at.isoformat() if self.failed_at else None,
95
+ 'enqueued_at': optional_datetime_str(self.enqueued_at),
96
+ 'started_at': optional_datetime_str(self.started_at),
97
+ 'failed_at': optional_datetime_str(self.failed_at),
92
98
  'feedback': self.feedback,
93
- 'completed_at': self.completed_at.isoformat() if self.completed_at else None,
94
99
  'metadata': self.metadata,
100
+ 'completed_at': optional_datetime_str(self.completed_at),
101
+ 'updated_at': optional_datetime_str(self.updated_at),
95
102
  'items': [item.to_dict for item in self.items],
96
103
  }
97
104
 
98
105
  @property
99
106
  def to_queue_dict(self) -> dict:
100
- data = self.to_dict
107
+ data = self.to_dict.copy()
101
108
  data["items"] = [
102
109
  item.to_queue_dict for item in self.items
103
110
  ]
@@ -105,7 +112,7 @@ class ProcessingCase(object):
105
112
 
106
113
  @property
107
114
  def to_persist_dict(self) -> dict:
108
- persist_data = self.to_dict
115
+ persist_data = self.to_dict.copy()
109
116
  persist_data["items"] = [
110
117
  item.to_dict for item in self.items
111
118
  ]
@@ -183,8 +190,6 @@ class ProcessingCase(object):
183
190
  else:
184
191
  self.status = ProcessingStatus.PENDING
185
192
 
186
-
187
-
188
193
  @classmethod
189
194
  def from_dict(cls, data: dict) -> 'ProcessingCase':
190
195
  return cls(
@@ -202,6 +207,8 @@ class ProcessingCase(object):
202
207
  feedback=data.get('feedback'),
203
208
  metadata=data.get('metadata', {}),
204
209
  completed_at=get_datetime_from_data(input_datetime=data.get('completed_at')),
210
+ created_at=get_datetime_from_data(input_datetime=data.get('created_at')),
211
+ updated_at=get_datetime_from_data(input_datetime=data.get('updated_at')),
205
212
  items=[
206
213
  ProcessingCaseItem.from_dict(item_dict)
207
214
  for item_dict in data.get('items', [])
@@ -216,4 +223,3 @@ class ProcessingCase(object):
216
223
  for item_dict in data.get('items', [])
217
224
  ]
218
225
  return instance
219
-
@@ -0,0 +1,63 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime
3
+ from typing import Optional
4
+ from uuid import UUID
5
+
6
+ from documente_shared.application.time_utils import get_datetime_from_data
7
+
8
+
9
+ @dataclass
10
+ class Workspace(object):
11
+ uuid: UUID
12
+ name: str
13
+ tenant_slug: str
14
+ is_archived: bool = False
15
+ metadata: Optional[dict] = None
16
+ created_at: Optional[datetime] = None
17
+ updated_at: Optional[datetime] = None
18
+ document_types: Optional[list] = None
19
+
20
+ def __post_init__(self):
21
+ self.metadata = self.metadata or {}
22
+ self.document_types = self.document_types or []
23
+
24
+ @property
25
+ def to_persist_dict(self) -> dict:
26
+ return {
27
+ "tenant_slug": self.tenant_slug,
28
+ "name": self.name,
29
+ "is_archived": self.is_archived,
30
+ "metadata": self.metadata or {},
31
+ }
32
+
33
+ @property
34
+ def to_dict(self) -> dict:
35
+ return {
36
+ "uuid": str(self.uuid),
37
+ "name": self.name,
38
+ "tenant_slug": self.tenant_slug,
39
+ "is_archived": self.is_archived,
40
+ "metadata": self.metadata,
41
+ "created_at": (
42
+ self.created_at.isoformat()
43
+ if self.created_at else None
44
+ ),
45
+ "updated_at": (
46
+ self.updated_at.isoformat()
47
+ if self.updated_at else None
48
+ ),
49
+ "document_types": self.document_types,
50
+ }
51
+
52
+ @classmethod
53
+ def from_dict(cls, data: dict) -> 'Workspace':
54
+ return cls(
55
+ uuid=data.get('uuid'),
56
+ name=data.get('name'),
57
+ tenant_slug=data.get('tenant_slug'),
58
+ is_archived=data.get('is_archived', False),
59
+ metadata=data.get('metadata', {}),
60
+ created_at=get_datetime_from_data(input_datetime=data.get('created_at')),
61
+ updated_at=get_datetime_from_data(input_datetime=data.get('updated_at')),
62
+ document_types=data.get('document_types', []),
63
+ )
@@ -0,0 +1,138 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime
3
+ from decimal import Decimal
4
+ from typing import Optional
5
+ from uuid import UUID
6
+
7
+ from documente_shared.application.time_utils import get_datetime_from_data
8
+ from documente_shared.domain.entities.in_memory_document import InMemoryDocument
9
+ from documente_shared.domain.enums.common import ProcessingStatus
10
+ from documente_shared.domain.enums.workspace import WorkspaceSource
11
+ from documente_shared.domain.helpers.values import optional_str
12
+
13
+
14
+ @dataclass
15
+ class WorkspaceDocument(object):
16
+ uuid: UUID
17
+ name: str
18
+ status: ProcessingStatus
19
+ tenant_slug: str
20
+ workspace_id: UUID
21
+ digest: Optional[str] = None
22
+ document_type_id: Optional[UUID] = None
23
+ source: Optional[WorkspaceSource] = None
24
+ document: Optional[InMemoryDocument] = None
25
+ document_url: Optional[str] = None
26
+ processing_time: Optional[Decimal] = None
27
+ metadata: Optional[dict] = None
28
+ extraction: Optional[dict] = None
29
+ uploaded_at: Optional[datetime] = None
30
+ created_at: Optional[datetime] = None
31
+ updated_at: Optional[datetime] = None
32
+ started_at: Optional[datetime] = None
33
+ completed_at: Optional[datetime] = None
34
+ failed_at: Optional[datetime] = None
35
+
36
+ def __post_init__(self):
37
+ self.metadata = self.metadata or {}
38
+ self.extraction = self.extraction or {}
39
+
40
+ @property
41
+ def to_persist_dict(self) -> dict:
42
+ return {
43
+ "tenant_slug": self.tenant_slug,
44
+ "workspace_id": str(self.workspace_id),
45
+ "doctype_id": str(self.document_type_id) if self.document_type_id else None,
46
+ "name": self.name,
47
+ "digest": self.digest,
48
+ "status": str(self.status),
49
+ "source": optional_str(self.source),
50
+ "uploaded_at": self.uploaded_at,
51
+ "started_at": self.started_at,
52
+ "completed_at": self.completed_at,
53
+ "failed_at": self.failed_at,
54
+ "processing_time": self.processing_time,
55
+ "metadata": self.metadata or {},
56
+ "extraction": self.extraction or {},
57
+ }
58
+
59
+ @property
60
+ def to_dict(self) -> dict:
61
+ return {
62
+ "uuid": str(self.uuid),
63
+ "name": self.name,
64
+ "digest": self.digest,
65
+ "status": str(self.status),
66
+ "tenant_slug": self.tenant_slug,
67
+ "workspace_id": str(self.workspace_id),
68
+ "document_type_id": str(self.document_type_id) if self.document_type_id else None,
69
+ "source": optional_str(self.source),
70
+ "document": (
71
+ self.document.to_dict
72
+ if self.document else None
73
+ ),
74
+ "document_url": self.document_url,
75
+ "processing_time": (
76
+ str(self.processing_time)
77
+ if self.processing_time else None
78
+ ),
79
+ "metadata": self.metadata,
80
+ "extraction": self.extraction,
81
+ "uploaded_at": (
82
+ self.uploaded_at.isoformat()
83
+ if self.uploaded_at else None
84
+ ),
85
+ "created_at": (
86
+ self.created_at.isoformat()
87
+ if self.created_at else None
88
+ ),
89
+ "updated_at": (
90
+ self.updated_at.isoformat()
91
+ if self.updated_at else None
92
+ ),
93
+ "started_at": (
94
+ self.started_at.isoformat()
95
+ if self.started_at else None
96
+ ),
97
+ "completed_at": (
98
+ self.completed_at.isoformat()
99
+ if self.completed_at else None
100
+ ),
101
+ "failed_at": (
102
+ self.failed_at.isoformat()
103
+ if self.failed_at else None
104
+ ),
105
+ }
106
+
107
+ @classmethod
108
+ def from_dict(cls, data: dict) -> 'WorkspaceDocument':
109
+ return cls(
110
+ uuid=data.get('uuid'),
111
+ name=data.get('name'),
112
+ digest=data.get('digest'),
113
+ status=ProcessingStatus.from_value(data.get('status')),
114
+ tenant_slug=data.get('tenant_slug'),
115
+ workspace_id=data.get('workspace_id'),
116
+ document_type_id=data.get('document_type_id'),
117
+ source=(
118
+ WorkspaceSource.from_value(data.get('source'))
119
+ if data.get('source') else None
120
+ ),
121
+ document=(
122
+ InMemoryDocument.from_dict(data.get('document'))
123
+ if data.get('document') else None
124
+ ),
125
+ document_url=data.get('document_url'),
126
+ processing_time=(
127
+ Decimal(data.get('processing_time'))
128
+ if data.get('processing_time') else None
129
+ ),
130
+ metadata=data.get('metadata', {}),
131
+ extraction=data.get('extraction', {}),
132
+ uploaded_at=get_datetime_from_data(input_datetime=data.get('uploaded_at')),
133
+ created_at=get_datetime_from_data(input_datetime=data.get('created_at')),
134
+ updated_at=get_datetime_from_data(input_datetime=data.get('updated_at')),
135
+ started_at=get_datetime_from_data(input_datetime=data.get('started_at')),
136
+ completed_at=get_datetime_from_data(input_datetime=data.get('completed_at')),
137
+ failed_at=get_datetime_from_data(input_datetime=data.get('failed_at')),
138
+ )
@@ -0,0 +1,45 @@
1
+ from dataclasses import dataclass
2
+ from typing import List, Optional
3
+ from uuid import UUID
4
+
5
+ from documente_shared.application.query_params import QueryParams
6
+ from documente_shared.domain.enums.common import ProcessingStatus
7
+
8
+
9
+ @dataclass
10
+ class WorkspaceDocumentFilters(object):
11
+ workspace_id: Optional[UUID] = None
12
+ sort_order: Optional[str] = None
13
+ search: Optional[str] = None
14
+ digest: Optional[str] = None
15
+ statuses: List[ProcessingStatus] = None
16
+
17
+ def __post_init__(self):
18
+ self.statuses = self.statuses or []
19
+ self.sort_order = self.sort_order or "desc"
20
+
21
+ @property
22
+ def to_params(self) -> dict:
23
+ params = {
24
+ "workspace_id": str(self.workspace_id) if self.workspace_id else None,
25
+ "sort": self.sort_order,
26
+ "search": self.search,
27
+ "digest": self.digest,
28
+ "statuses": ",".join(str(s) for s in self.statuses) if self.statuses else None,
29
+ }
30
+ return {k: v for k, v in params.items() if v is not None}
31
+
32
+ @classmethod
33
+ def from_params(cls, params: QueryParams) -> "WorkspaceDocumentFilters":
34
+ search_term = params.get_str(key="search", default=None)
35
+ return cls(
36
+ workspace_id=params.get_uuid(key="workspace_id", default=None),
37
+ sort_order=params.get(key="sort", default="desc"),
38
+ search=search_term.strip() if search_term else None,
39
+ digest=params.get_str(key="digest", default=None),
40
+ statuses=params.get_enum_list(
41
+ key="statuses",
42
+ enum_class=ProcessingStatus,
43
+ default=None,
44
+ ),
45
+ )
@@ -0,0 +1,70 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime
3
+ from typing import Optional
4
+ from uuid import UUID
5
+
6
+ from documente_shared.application.time_utils import get_datetime_from_data
7
+ from documente_shared.domain.entities.in_memory_document import InMemoryDocument
8
+
9
+
10
+ @dataclass
11
+ class WorkspaceDocumentPage(object):
12
+ uuid: UUID
13
+ tenant_slug: str
14
+ workspace_id: UUID
15
+ workspace_document_id: UUID
16
+ page_number: int
17
+ page_file: Optional[InMemoryDocument] = None
18
+ page_file_url: Optional[str] = None
19
+ created_at: Optional[datetime] = None
20
+ synced_at: Optional[datetime] = None
21
+
22
+ @property
23
+ def to_persist_dict(self) -> dict:
24
+ return {
25
+ "tenant_slug": self.tenant_slug,
26
+ "workspace_id": str(self.workspace_id),
27
+ "workspace_document_id": str(self.workspace_document_id),
28
+ "page_number": self.page_number,
29
+ "synced_at": self.synced_at,
30
+ }
31
+
32
+ @property
33
+ def to_dict(self) -> dict:
34
+ return {
35
+ "uuid": str(self.uuid),
36
+ "tenant_slug": self.tenant_slug,
37
+ "workspace_id": str(self.workspace_id),
38
+ "workspace_document_id": str(self.workspace_document_id),
39
+ "page_number": self.page_number,
40
+ "page_file": (
41
+ self.page_file.to_dict
42
+ if self.page_file else None
43
+ ),
44
+ "page_file_url": self.page_file_url,
45
+ "created_at": (
46
+ self.created_at.isoformat()
47
+ if self.created_at else None
48
+ ),
49
+ "synced_at": (
50
+ self.synced_at.isoformat()
51
+ if self.synced_at else None
52
+ ),
53
+ }
54
+
55
+ @classmethod
56
+ def from_dict(cls, data: dict) -> 'WorkspaceDocumentPage':
57
+ return cls(
58
+ uuid=data.get('uuid'),
59
+ tenant_slug=data.get('tenant_slug'),
60
+ workspace_id=data.get('workspace_id'),
61
+ workspace_document_id=data.get('workspace_document_id'),
62
+ page_number=data.get('page_number'),
63
+ page_file=(
64
+ InMemoryDocument.from_dict(data.get('page_file'))
65
+ if data.get('page_file') else None
66
+ ),
67
+ page_file_url=data.get('page_file_url'),
68
+ created_at=get_datetime_from_data(input_datetime=data.get('created_at')),
69
+ synced_at=get_datetime_from_data(input_datetime=data.get('synced_at')),
70
+ )
@@ -0,0 +1,32 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+ from uuid import UUID
4
+
5
+ from documente_shared.application.query_params import QueryParams
6
+
7
+
8
+ @dataclass
9
+ class WorkspaceDocumentPageFilters(object):
10
+ workspace_document_id: Optional[UUID] = None
11
+ sort_order: Optional[str] = None
12
+ page_number: Optional[int] = None
13
+
14
+ def __post_init__(self):
15
+ self.sort_order = self.sort_order or "desc"
16
+
17
+ @property
18
+ def to_params(self) -> dict:
19
+ params = {
20
+ "workspace_document_id": str(self.workspace_document_id) if self.workspace_document_id else None,
21
+ "sort": self.sort_order,
22
+ "page_number": self.page_number,
23
+ }
24
+ return {k: v for k, v in params.items() if v is not None}
25
+
26
+ @classmethod
27
+ def from_params(cls, params: QueryParams) -> "WorkspaceDocumentPageFilters":
28
+ return cls(
29
+ workspace_document_id=params.get_uuid(key="workspace_document_id", default=None),
30
+ sort_order=params.get(key="sort", default="desc"),
31
+ page_number=params.get_int(key="page_number", default=None),
32
+ )
@@ -0,0 +1,35 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+ from documente_shared.application.query_params import QueryParams
5
+
6
+
7
+ @dataclass
8
+ class WorkspaceFilters(object):
9
+ sort_order: Optional[str] = None
10
+ search: Optional[str] = None
11
+ include_archived: bool = False
12
+
13
+ def __post_init__(self):
14
+ self.sort_order = self.sort_order or "desc"
15
+
16
+ @property
17
+ def to_params(self) -> dict:
18
+ params = {
19
+ "sort": self.sort_order,
20
+ "search": self.search,
21
+ "include_archived": str(self.include_archived).lower(),
22
+ }
23
+ return {k: v for k, v in params.items() if v is not None}
24
+
25
+ @classmethod
26
+ def from_params(cls, params: QueryParams) -> "WorkspaceFilters":
27
+ search_term = params.get_str(key="search", default=None)
28
+ return cls(
29
+ sort_order=params.get(key="sort", default="desc"),
30
+ search=search_term.strip() if search_term else None,
31
+ include_archived=params.get_bool(
32
+ key="include_archived",
33
+ default=False,
34
+ ),
35
+ )
@@ -0,0 +1,19 @@
1
+ from documente_shared.domain.base_enum import BaseEnum
2
+
3
+
4
+ class WorkspaceSource(BaseEnum):
5
+ WEB = "WEB"
6
+ MOBILE = "MOBILE"
7
+ TOOLS = "TOOLS"
8
+
9
+ @property
10
+ def is_web(self):
11
+ return self == self.WEB
12
+
13
+ @property
14
+ def is_mobile(self):
15
+ return self == self.MOBILE
16
+
17
+ @property
18
+ def is_tools(self):
19
+ return self == self.TOOLS
@@ -0,0 +1,13 @@
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()
@@ -0,0 +1,28 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, List, Optional
3
+ from uuid import UUID
4
+
5
+ from documente_shared.domain.entities.workspace import Workspace
6
+ from documente_shared.domain.entities.workspace_filters import WorkspaceFilters
7
+
8
+
9
+ class WorkspaceRepository(ABC):
10
+ @abstractmethod
11
+ def find(self, uuid: UUID) -> Optional[Workspace]:
12
+ raise NotImplementedError
13
+
14
+ @abstractmethod
15
+ def filter(self, filters: WorkspaceFilters) -> List[Workspace]:
16
+ raise NotImplementedError
17
+
18
+ @abstractmethod
19
+ def filter_paginated(self, filters: WorkspaceFilters, page_params: Any) -> Any:
20
+ raise NotImplementedError
21
+
22
+ @abstractmethod
23
+ def persist(self, instance: Workspace) -> Workspace:
24
+ raise NotImplementedError
25
+
26
+ @abstractmethod
27
+ def delete(self, uuid: UUID) -> None:
28
+ raise NotImplementedError
@@ -0,0 +1,28 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, List, Optional
3
+ from uuid import UUID
4
+
5
+ from documente_shared.domain.entities.workspace_document import WorkspaceDocument
6
+ from documente_shared.domain.entities.workspace_document_filters import WorkspaceDocumentFilters
7
+
8
+
9
+ class WorkspaceDocumentRepository(ABC):
10
+ @abstractmethod
11
+ def find(self, uuid: UUID) -> Optional[WorkspaceDocument]:
12
+ raise NotImplementedError
13
+
14
+ @abstractmethod
15
+ def filter(self, filters: WorkspaceDocumentFilters) -> List[WorkspaceDocument]:
16
+ raise NotImplementedError
17
+
18
+ @abstractmethod
19
+ def filter_paginated(self, filters: WorkspaceDocumentFilters, page_params: Any) -> Any:
20
+ raise NotImplementedError
21
+
22
+ @abstractmethod
23
+ def persist(self, instance: WorkspaceDocument) -> WorkspaceDocument:
24
+ raise NotImplementedError
25
+
26
+ @abstractmethod
27
+ def delete(self, uuid: UUID) -> None:
28
+ raise NotImplementedError
@@ -0,0 +1,28 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, List, Optional
3
+ from uuid import UUID
4
+
5
+ from documente_shared.domain.entities.workspace_document_page import WorkspaceDocumentPage
6
+ from documente_shared.domain.entities.workspace_document_page_filters import WorkspaceDocumentPageFilters
7
+
8
+
9
+ class WorkspaceDocumentPageRepository(ABC):
10
+ @abstractmethod
11
+ def find(self, uuid: UUID) -> Optional[WorkspaceDocumentPage]:
12
+ raise NotImplementedError
13
+
14
+ @abstractmethod
15
+ def filter(self, filters: WorkspaceDocumentPageFilters) -> List[WorkspaceDocumentPage]:
16
+ raise NotImplementedError
17
+
18
+ @abstractmethod
19
+ def filter_paginated(self, filters: WorkspaceDocumentPageFilters, page_params: Any) -> Any:
20
+ raise NotImplementedError
21
+
22
+ @abstractmethod
23
+ def persist(self, instance: WorkspaceDocumentPage) -> WorkspaceDocumentPage:
24
+ raise NotImplementedError
25
+
26
+ @abstractmethod
27
+ def delete(self, uuid: UUID) -> None:
28
+ raise NotImplementedError
@@ -0,0 +1,71 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, List, Optional
3
+ from uuid import UUID
4
+
5
+ from loguru import logger
6
+ from requests import Response
7
+
8
+ from documente_shared.application.payloads import camel_to_snake
9
+ from documente_shared.domain.entities.workspace import Workspace
10
+ from documente_shared.domain.entities.workspace_filters import WorkspaceFilters
11
+ from documente_shared.domain.repositories.workspace import WorkspaceRepository
12
+ from documente_shared.infrastructure.documente_client import DocumenteClientMixin
13
+
14
+
15
+ @dataclass
16
+ class HttpWorkspaceRepository(
17
+ DocumenteClientMixin,
18
+ WorkspaceRepository,
19
+ ):
20
+ def find(self, uuid: UUID) -> Optional[Workspace]:
21
+ response = self.session.get(
22
+ url=f"{self.api_url}/v1/workspaces/{uuid}/",
23
+ )
24
+ if response.status_code not in [200, 201]:
25
+ return None
26
+ return self._build_workspace(response)
27
+
28
+ def filter(self, filters: WorkspaceFilters) -> List[Workspace]:
29
+ response = self.session.get(
30
+ url=f"{self.api_url}/v1/workspaces/",
31
+ params=filters.to_params,
32
+ )
33
+ if response.status_code not in [200, 201]:
34
+ return []
35
+ raw_response = response.json()
36
+ return [
37
+ Workspace.from_dict(camel_to_snake(item_data))
38
+ for item_data in raw_response.get('data', [])
39
+ ]
40
+
41
+ def filter_paginated(self, filters: WorkspaceFilters, page_params: Any) -> Any:
42
+ params = {**filters.to_params}
43
+ if isinstance(page_params, dict):
44
+ params.update(page_params)
45
+ response = self.session.get(
46
+ url=f"{self.api_url}/v1/workspaces/",
47
+ params=params,
48
+ )
49
+ if response.status_code not in [200, 201]:
50
+ return {"data": [], "count": 0}
51
+ return response.json()
52
+
53
+ def persist(self, instance: Workspace) -> Workspace:
54
+ logger.info(f"PERSISTING_WORKSPACE: data={instance.to_persist_dict}")
55
+ response: Response = self.session.post(
56
+ url=f"{self.api_url}/v1/workspaces/",
57
+ json=instance.to_persist_dict,
58
+ )
59
+ if response.status_code not in [200, 201]:
60
+ logger.info(f"PERSISTING_WORKSPACE ERROR: data={response.text}")
61
+ return instance
62
+ return self._build_workspace(response)
63
+
64
+ def delete(self, uuid: UUID) -> None:
65
+ self.session.delete(f"{self.api_url}/v1/workspaces/{uuid}/")
66
+
67
+ @classmethod
68
+ def _build_workspace(cls, response: Response) -> Workspace:
69
+ response_json = response.json()
70
+ instance_data = response_json.get('data', {})
71
+ return Workspace.from_dict(camel_to_snake(instance_data))
@@ -0,0 +1,71 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, List, Optional
3
+ from uuid import UUID
4
+
5
+ from loguru import logger
6
+ from requests import Response
7
+
8
+ from documente_shared.application.payloads import camel_to_snake
9
+ from documente_shared.domain.entities.workspace_document import WorkspaceDocument
10
+ from documente_shared.domain.entities.workspace_document_filters import WorkspaceDocumentFilters
11
+ from documente_shared.domain.repositories.workspace_document import WorkspaceDocumentRepository
12
+ from documente_shared.infrastructure.documente_client import DocumenteClientMixin
13
+
14
+
15
+ @dataclass
16
+ class HttpWorkspaceDocumentRepository(
17
+ DocumenteClientMixin,
18
+ WorkspaceDocumentRepository,
19
+ ):
20
+ def find(self, uuid: UUID) -> Optional[WorkspaceDocument]:
21
+ response = self.session.get(
22
+ url=f"{self.api_url}/v1/workspace-documents/{uuid}/",
23
+ )
24
+ if response.status_code not in [200, 201]:
25
+ return None
26
+ return self._build_workspace_document(response)
27
+
28
+ def filter(self, filters: WorkspaceDocumentFilters) -> List[WorkspaceDocument]:
29
+ response = self.session.get(
30
+ url=f"{self.api_url}/v1/workspaces/{filters.workspace_id}/documents/",
31
+ params=filters.to_params,
32
+ )
33
+ if response.status_code not in [200, 201]:
34
+ return []
35
+ raw_response = response.json()
36
+ return [
37
+ WorkspaceDocument.from_dict(camel_to_snake(item_data))
38
+ for item_data in raw_response.get('data', [])
39
+ ]
40
+
41
+ def filter_paginated(self, filters: WorkspaceDocumentFilters, page_params: Any) -> Any:
42
+ params = {**filters.to_params}
43
+ if isinstance(page_params, dict):
44
+ params.update(page_params)
45
+ response = self.session.get(
46
+ url=f"{self.api_url}/v1/workspaces/{filters.workspace_id}/documents/",
47
+ params=params,
48
+ )
49
+ if response.status_code not in [200, 201]:
50
+ return {"data": [], "count": 0}
51
+ return response.json()
52
+
53
+ def persist(self, instance: WorkspaceDocument) -> WorkspaceDocument:
54
+ logger.info(f"PERSISTING_WORKSPACE_DOCUMENT: data={instance.to_persist_dict}")
55
+ response: Response = self.session.post(
56
+ url=f"{self.api_url}/v1/workspaces/{instance.workspace_id}/documents/",
57
+ json=instance.to_persist_dict,
58
+ )
59
+ if response.status_code not in [200, 201]:
60
+ logger.info(f"PERSISTING_WORKSPACE_DOCUMENT ERROR: data={response.text}")
61
+ return instance
62
+ return self._build_workspace_document(response)
63
+
64
+ def delete(self, uuid: UUID) -> None:
65
+ self.session.delete(f"{self.api_url}/v1/workspace-documents/{uuid}/")
66
+
67
+ @classmethod
68
+ def _build_workspace_document(cls, response: Response) -> WorkspaceDocument:
69
+ response_json = response.json()
70
+ instance_data = response_json.get('data', {})
71
+ return WorkspaceDocument.from_dict(camel_to_snake(instance_data))
@@ -0,0 +1,71 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, List, Optional
3
+ from uuid import UUID
4
+
5
+ from loguru import logger
6
+ from requests import Response
7
+
8
+ from documente_shared.application.payloads import camel_to_snake
9
+ from documente_shared.domain.entities.workspace_document_page import WorkspaceDocumentPage
10
+ from documente_shared.domain.entities.workspace_document_page_filters import WorkspaceDocumentPageFilters
11
+ from documente_shared.domain.repositories.workspace_document_page import WorkspaceDocumentPageRepository
12
+ from documente_shared.infrastructure.documente_client import DocumenteClientMixin
13
+
14
+
15
+ @dataclass
16
+ class HttpWorkspaceDocumentPageRepository(
17
+ DocumenteClientMixin,
18
+ WorkspaceDocumentPageRepository,
19
+ ):
20
+ def find(self, uuid: UUID) -> Optional[WorkspaceDocumentPage]:
21
+ response = self.session.get(
22
+ url=f"{self.api_url}/v1/workspace-document-pages/{uuid}/",
23
+ )
24
+ if response.status_code not in [200, 201]:
25
+ return None
26
+ return self._build_workspace_document_page(response)
27
+
28
+ def filter(self, filters: WorkspaceDocumentPageFilters) -> List[WorkspaceDocumentPage]:
29
+ response = self.session.get(
30
+ url=f"{self.api_url}/v1/workspace-documents/{filters.workspace_document_id}/pages/",
31
+ params=filters.to_params,
32
+ )
33
+ if response.status_code not in [200, 201]:
34
+ return []
35
+ raw_response = response.json()
36
+ return [
37
+ WorkspaceDocumentPage.from_dict(camel_to_snake(item_data))
38
+ for item_data in raw_response.get('data', [])
39
+ ]
40
+
41
+ def filter_paginated(self, filters: WorkspaceDocumentPageFilters, page_params: Any) -> Any:
42
+ params = {**filters.to_params}
43
+ if isinstance(page_params, dict):
44
+ params.update(page_params)
45
+ response = self.session.get(
46
+ url=f"{self.api_url}/v1/workspace-documents/{filters.workspace_document_id}/pages/",
47
+ params=params,
48
+ )
49
+ if response.status_code not in [200, 201]:
50
+ return {"data": [], "count": 0}
51
+ return response.json()
52
+
53
+ def persist(self, instance: WorkspaceDocumentPage) -> WorkspaceDocumentPage:
54
+ logger.info(f"PERSISTING_WORKSPACE_DOCUMENT_PAGE: data={instance.to_persist_dict}")
55
+ response: Response = self.session.post(
56
+ url=f"{self.api_url}/v1/workspace-documents/{instance.workspace_document_id}/pages/",
57
+ json=instance.to_persist_dict,
58
+ )
59
+ if response.status_code not in [200, 201]:
60
+ logger.info(f"PERSISTING_WORKSPACE_DOCUMENT_PAGE ERROR: data={response.text}")
61
+ return instance
62
+ return self._build_workspace_document_page(response)
63
+
64
+ def delete(self, uuid: UUID) -> None:
65
+ self.session.delete(f"{self.api_url}/v1/workspace-document-pages/{uuid}/")
66
+
67
+ @classmethod
68
+ def _build_workspace_document_page(cls, response: Response) -> WorkspaceDocumentPage:
69
+ response_json = response.json()
70
+ instance_data = response_json.get('data', {})
71
+ return WorkspaceDocumentPage.from_dict(camel_to_snake(instance_data))
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "documente_shared"
3
- version = "0.1.159"
3
+ version = "0.1.161"
4
4
  description = "Shared utilities for Documente AI projects"
5
5
  authors = [{ name = "Tech", email = "tech@llamitai.com" }]
6
6
  license = { text = "MIT" }
@@ -129,7 +129,7 @@ wheels = [
129
129
 
130
130
  [[package]]
131
131
  name = "documente-shared"
132
- version = "0.1.159"
132
+ version = "0.1.161"
133
133
  source = { editable = "." }
134
134
  dependencies = [
135
135
  { name = "boto3" },