documente_shared 0.1.145__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 (63) hide show
  1. documente_shared/__init__.py +0 -0
  2. documente_shared/application/__init__.py +0 -0
  3. documente_shared/application/dates.py +7 -0
  4. documente_shared/application/digest.py +7 -0
  5. documente_shared/application/exceptions.py +23 -0
  6. documente_shared/application/files.py +27 -0
  7. documente_shared/application/json.py +45 -0
  8. documente_shared/application/numbers.py +7 -0
  9. documente_shared/application/payloads.py +29 -0
  10. documente_shared/application/query_params.py +133 -0
  11. documente_shared/application/retry_utils.py +69 -0
  12. documente_shared/application/time_utils.py +13 -0
  13. documente_shared/application/timezone.py +7 -0
  14. documente_shared/domain/__init__.py +0 -0
  15. documente_shared/domain/base_enum.py +54 -0
  16. documente_shared/domain/constants.py +8 -0
  17. documente_shared/domain/entities/__init__.py +0 -0
  18. documente_shared/domain/entities/document.py +410 -0
  19. documente_shared/domain/entities/document_metadata.py +64 -0
  20. documente_shared/domain/entities/in_memory_document.py +75 -0
  21. documente_shared/domain/entities/processing_case.py +215 -0
  22. documente_shared/domain/entities/processing_case_filters.py +51 -0
  23. documente_shared/domain/entities/processing_case_item.py +300 -0
  24. documente_shared/domain/entities/processing_case_item_filters.py +54 -0
  25. documente_shared/domain/entities/processing_documents.py +11 -0
  26. documente_shared/domain/entities/processing_event.py +71 -0
  27. documente_shared/domain/entities/scaling.py +31 -0
  28. documente_shared/domain/enums/__init__.py +0 -0
  29. documente_shared/domain/enums/circular_oficio.py +29 -0
  30. documente_shared/domain/enums/common.py +133 -0
  31. documente_shared/domain/enums/document.py +124 -0
  32. documente_shared/domain/enums/document_type_record.py +13 -0
  33. documente_shared/domain/enums/processing_case.py +66 -0
  34. documente_shared/domain/exceptions.py +5 -0
  35. documente_shared/domain/interfaces/__init__.py +0 -0
  36. documente_shared/domain/interfaces/scaling.py +10 -0
  37. documente_shared/domain/repositories/__init__.py +0 -0
  38. documente_shared/domain/repositories/document.py +24 -0
  39. documente_shared/domain/repositories/processing_case.py +36 -0
  40. documente_shared/domain/repositories/processing_case_item.py +49 -0
  41. documente_shared/infrastructure/__init__.py +0 -0
  42. documente_shared/infrastructure/documente_client.py +27 -0
  43. documente_shared/infrastructure/dynamo_table.py +75 -0
  44. documente_shared/infrastructure/lambdas.py +14 -0
  45. documente_shared/infrastructure/repositories/__init__.py +0 -0
  46. documente_shared/infrastructure/repositories/dynamo_document.py +43 -0
  47. documente_shared/infrastructure/repositories/dynamo_processing_case.py +55 -0
  48. documente_shared/infrastructure/repositories/dynamo_processing_case_item.py +70 -0
  49. documente_shared/infrastructure/repositories/http_document.py +66 -0
  50. documente_shared/infrastructure/repositories/http_processing_case.py +82 -0
  51. documente_shared/infrastructure/repositories/http_processing_case_item.py +118 -0
  52. documente_shared/infrastructure/repositories/mem_document.py +46 -0
  53. documente_shared/infrastructure/repositories/mem_processing_case.py +44 -0
  54. documente_shared/infrastructure/repositories/mem_processing_case_item.py +52 -0
  55. documente_shared/infrastructure/s3_bucket.py +58 -0
  56. documente_shared/infrastructure/services/__init__.py +0 -0
  57. documente_shared/infrastructure/services/http_scaling.py +25 -0
  58. documente_shared/infrastructure/sqs_queue.py +48 -0
  59. documente_shared/presentation/__init__.py +0 -0
  60. documente_shared/presentation/presenters.py +16 -0
  61. documente_shared-0.1.145.dist-info/METADATA +39 -0
  62. documente_shared-0.1.145.dist-info/RECORD +63 -0
  63. documente_shared-0.1.145.dist-info/WHEEL +4 -0
@@ -0,0 +1,215 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime, tzinfo
3
+ from typing import Optional, List
4
+
5
+ from documente_shared.application.time_utils import get_datetime_from_data
6
+ from documente_shared.domain.constants import la_paz_tz
7
+ from documente_shared.domain.entities.processing_case_item import ProcessingCaseItem
8
+ from documente_shared.domain.enums.common import ProcessingStatus
9
+ from documente_shared.domain.enums.processing_case import ProcessingCaseType
10
+
11
+
12
+ @dataclass
13
+ class ProcessingCase(object):
14
+ uuid: str
15
+ name: str
16
+ tenant_slug: str
17
+ status: ProcessingStatus
18
+ case_type: ProcessingCaseType
19
+ enqueued_at: Optional[datetime] = None
20
+ started_at: Optional[datetime] = None
21
+ failed_at: Optional[datetime] = None
22
+ feedback: Optional[list | dict] = None
23
+ completed_at: Optional[datetime] = None
24
+ metadata: Optional[dict] = None
25
+ items: Optional[List[ProcessingCaseItem]] = None
26
+
27
+ def __post_init__(self):
28
+ self.items = self.items or []
29
+
30
+ def __eq__(self, other: 'ProcessingCase') -> bool:
31
+ if not other:
32
+ return False
33
+
34
+ return (
35
+ self.uuid == other.uuid
36
+ and self.name == other.name
37
+ and self.status == other.status
38
+ and self.case_type == other.case_type
39
+ and self.enqueued_at == other.enqueued_at
40
+ and self.started_at == other.started_at
41
+ and self.failed_at == other.failed_at
42
+ and self.feedback == other.feedback
43
+ and self.completed_at == other.completed_at
44
+ and self.metadata == other.metadata
45
+ )
46
+
47
+ @property
48
+ def strategy_id(self) ->str:
49
+ return str(self.case_type)
50
+
51
+ @property
52
+ def is_procesable(self) -> bool:
53
+ return self.items and len(self.items) > 0
54
+
55
+ @property
56
+ def is_queue_procesable(self) -> bool:
57
+ return len(self.pending_items) > 0
58
+
59
+ @property
60
+ def pending_items(self) -> List[ProcessingCaseItem]:
61
+ return [
62
+ item for item in self.items
63
+ if item.status == ProcessingStatus.PENDING
64
+ ]
65
+
66
+ @property
67
+ def is_bcp_microcredito(self) -> bool:
68
+ return self.case_type and self.case_type.is_bcp_microcredito
69
+
70
+ @property
71
+ def is_univida_soat(self) -> bool:
72
+ return self.case_type and self.case_type.is_univida_soat
73
+
74
+ @property
75
+ def to_dict(self) -> dict:
76
+ return {
77
+ 'uuid': self.uuid,
78
+ 'tenant_slug': self.tenant_slug,
79
+ 'name': self.name,
80
+ 'status': str(self.status),
81
+ 'case_type': (
82
+ str(self.case_type)
83
+ if self.case_type else None
84
+ ),
85
+ 'enqueued_at': self.enqueued_at.isoformat() if self.enqueued_at else None,
86
+ 'started_at': self.started_at.isoformat() if self.started_at else None,
87
+ 'failed_at': self.failed_at.isoformat() if self.failed_at else None,
88
+ 'feedback': self.feedback,
89
+ 'completed_at': self.completed_at.isoformat() if self.completed_at else None,
90
+ 'metadata': self.metadata,
91
+ 'items': [item.to_dict for item in self.items],
92
+ }
93
+
94
+ @property
95
+ def to_queue_dict(self) -> dict:
96
+ data = self.to_dict
97
+ data["items"] = [
98
+ item.to_queue_dict for item in self.items
99
+ ]
100
+ return data
101
+
102
+ @property
103
+ def to_persist_dict(self) -> dict:
104
+ persist_data = self.to_dict
105
+ persist_data["items"] = [
106
+ item.to_dict for item in self.items
107
+ ]
108
+ return persist_data
109
+
110
+ @property
111
+ def procesable_items(self) -> List[ProcessingCaseItem]:
112
+ return [
113
+ item for item in self.items
114
+ if item.status in [
115
+ ProcessingStatus.PENDING,
116
+ ProcessingStatus.ENQUEUED,
117
+ ]
118
+ ]
119
+
120
+ @property
121
+ def has_procesable_items(self) -> bool:
122
+ return len(self.procesable_items) > 0
123
+
124
+ def pending(self, timezone: tzinfo = la_paz_tz):
125
+ self.status = ProcessingStatus.PENDING
126
+ self.started_at = None
127
+
128
+ def enqueue(self, timezone: tzinfo = la_paz_tz):
129
+ self.status = ProcessingStatus.ENQUEUED
130
+ self.enqueued_at = datetime.now(tz=timezone)
131
+
132
+ def processing(self, timezone: tzinfo = la_paz_tz):
133
+ self.status = ProcessingStatus.PROCESSING
134
+ self.started_at = datetime.now(tz=timezone)
135
+
136
+ def failed(
137
+ self,
138
+ error_message: Optional[str] = None,
139
+ timezone: tzinfo = la_paz_tz,
140
+ ):
141
+ self.status = ProcessingStatus.FAILED
142
+ self.failed_at = datetime.now(tz=timezone)
143
+
144
+ def incomplete(self, timezone: tzinfo = la_paz_tz):
145
+ self.status = ProcessingStatus.INCOMPLETE
146
+ self.updated_at = datetime.now(tz=timezone)
147
+
148
+ def in_reviewed(self, timezone: tzinfo = la_paz_tz):
149
+ self.status = ProcessingStatus.IN_REVIEW
150
+ self.updated_at = datetime.now(tz=timezone)
151
+
152
+ def cancelled(self, timezone: tzinfo = la_paz_tz):
153
+ self.status = ProcessingStatus.CANCELLED
154
+ self.updated_at = datetime.now(tz=timezone)
155
+
156
+ def completed(self, timezone: tzinfo = la_paz_tz):
157
+ self.status = ProcessingStatus.COMPLETED
158
+ self.completed_at = datetime.now(tz=timezone)
159
+
160
+ def deleted(self):
161
+ self.status = ProcessingStatus.DELETED
162
+
163
+ def refresh_status(self):
164
+ if not self.items:
165
+ return
166
+
167
+ item_statuses = [item.status for item in self.items]
168
+
169
+ if any(status == ProcessingStatus.FAILED for status in item_statuses):
170
+ self.status = ProcessingStatus.INCOMPLETE
171
+ elif any(status == ProcessingStatus.PROCESSING for status in item_statuses):
172
+ self.status = ProcessingStatus.PROCESSING
173
+ elif any(status == ProcessingStatus.INCOMPLETE for status in item_statuses): # ← AGREGAR ESTA LÍNEA
174
+ self.status = ProcessingStatus.INCOMPLETE
175
+ elif all(status == ProcessingStatus.COMPLETED for status in item_statuses):
176
+ self.status = ProcessingStatus.COMPLETED
177
+ elif all(status == ProcessingStatus.PENDING for status in item_statuses):
178
+ self.status = ProcessingStatus.PENDING
179
+ else:
180
+ self.status = ProcessingStatus.PENDING
181
+
182
+
183
+
184
+ @classmethod
185
+ def from_dict(cls, data: dict) -> 'ProcessingCase':
186
+ return cls(
187
+ uuid=data.get('uuid'),
188
+ name=data.get('name'),
189
+ tenant_slug=data.get('tenant_slug'),
190
+ status=ProcessingStatus.from_value(data.get('status')),
191
+ case_type=(
192
+ ProcessingCaseType.from_value(data.get('case_type'))
193
+ if data.get('case_type') else None
194
+ ),
195
+ enqueued_at=get_datetime_from_data(input_datetime=data.get('enqueued_at')),
196
+ started_at=get_datetime_from_data(input_datetime=data.get('started_at')),
197
+ failed_at=get_datetime_from_data(input_datetime=data.get('failed_at')),
198
+ feedback=data.get('feedback'),
199
+ metadata=data.get('metadata', {}),
200
+ completed_at=get_datetime_from_data(input_datetime=data.get('completed_at')),
201
+ items=[
202
+ ProcessingCaseItem.from_dict(item_dict)
203
+ for item_dict in data.get('items', [])
204
+ ],
205
+ )
206
+
207
+ @classmethod
208
+ def from_persist_dict(cls, data: dict) -> 'ProcessingCase':
209
+ instance = cls.from_dict(data)
210
+ instance.items = [
211
+ ProcessingCaseItem.from_persist_dict(item_dict)
212
+ for item_dict in data.get('items', [])
213
+ ]
214
+ return instance
215
+
@@ -0,0 +1,51 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime
3
+ from typing import List, Optional
4
+
5
+ from documente_shared.domain.enums.common import ProcessingStatus
6
+ from documente_shared.domain.enums.processing_case import ProcessingCaseType
7
+ from documente_shared.application.query_params import QueryParams
8
+
9
+
10
+ @dataclass
11
+ class ProcessingCaseFilters(object):
12
+ case_ids: Optional[List[str]] = None
13
+ sort_order: Optional[str] = None
14
+ search: Optional[str] = None
15
+ init_date: Optional[datetime] = None
16
+ end_date: Optional[datetime] = None
17
+ statuses: List[ProcessingStatus] = None
18
+ case_types: List[ProcessingCaseType] = None
19
+ include_archived: bool = False
20
+ tenant_slug: Optional[str] = None
21
+
22
+ def __post_init__(self):
23
+ self.case_ids = self.case_ids or []
24
+ self.statuses = self.statuses or []
25
+ self.case_types = self.case_types or []
26
+ self.sort_order = self.sort_order or "desc"
27
+
28
+ @classmethod
29
+ def from_params(cls, params: QueryParams) -> "ProcessingCaseFilters":
30
+ search_term = params.get_str(key="search", default=None)
31
+ return ProcessingCaseFilters(
32
+ case_ids=params.get_uuid_list(key="case_ids", default=None),
33
+ sort_order=params.get(key="sort", default="desc"),
34
+ search=search_term.strip() if search_term else None,
35
+ init_date=params.get_datetime(key="init_date", default=None),
36
+ end_date=params.get_datetime(key="end_date", default=None),
37
+ statuses=params.get_enum_list(
38
+ key="statuses",
39
+ enum_class=ProcessingStatus,
40
+ default=None,
41
+ ),
42
+ case_types=params.get_enum_list(
43
+ key="case_types",
44
+ enum_class=ProcessingCaseType,
45
+ default=None,
46
+ ),
47
+ include_archived=params.get_bool(
48
+ key="include_archived",
49
+ default=False,
50
+ ),
51
+ )
@@ -0,0 +1,300 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime, tzinfo
3
+ from decimal import Decimal
4
+ from typing import Optional, List
5
+
6
+ from documente_shared.application.numbers import normalize_number
7
+ from documente_shared.application.time_utils import get_datetime_from_data
8
+ from documente_shared.domain.constants import la_paz_tz
9
+ from documente_shared.domain.entities.in_memory_document import InMemoryDocument
10
+ from documente_shared.domain.enums.common import ProcessingStatus, ProcessingSource
11
+ from documente_shared.domain.enums.processing_case import ProcessingDocumentType
12
+
13
+
14
+ @dataclass
15
+ class ProcessingCaseItem(object):
16
+ uuid: str
17
+ case_id: str
18
+ digest: str
19
+ status: ProcessingStatus
20
+ name: Optional[str] = None
21
+ document: Optional[InMemoryDocument] = None
22
+ document_type: Optional[ProcessingDocumentType] = None
23
+ uploaded_from: Optional[ProcessingSource] = None
24
+ processed_csv: Optional[InMemoryDocument] = None
25
+ processed_xlsx: Optional[InMemoryDocument] = None
26
+ processed_json: Optional[InMemoryDocument] = None
27
+ processing_time: Optional[Decimal] = None
28
+ processing_confidence: Optional[Decimal] = None
29
+ uploaded_at: Optional[datetime] = None
30
+ started_at: Optional[datetime] = None
31
+ failed_at: Optional[datetime] = None
32
+ completed_at: Optional[datetime] = None
33
+ feedback: Optional[list | dict] = None
34
+ metadata: Optional[dict] = None
35
+
36
+ def __post_init__(self):
37
+ self.feedback = self.feedback or []
38
+ self.metadata = self.metadata or {}
39
+
40
+
41
+ def __eq__(self, other: 'ProcessingCaseItem') -> bool:
42
+ if not other:
43
+ return False
44
+
45
+ return (
46
+ self.uuid == other.uuid
47
+ and self.digest == other.digest
48
+ and self.status == other.status
49
+ and self.document_type == other.document_type
50
+ and self.document == other.document
51
+ and self.processing_time == other.processing_time
52
+ and self.processing_confidence == other.processing_confidence
53
+ and self.uploaded_at == other.uploaded_at
54
+ and self.started_at == other.started_at
55
+ and self.failed_at == other.failed_at
56
+ and self.completed_at == other.completed_at
57
+ )
58
+
59
+ def pending(self, timezone: tzinfo = la_paz_tz):
60
+ self.status = ProcessingStatus.PENDING
61
+ self.started_at = None
62
+
63
+ def processing(self, timezone: tzinfo = la_paz_tz):
64
+ self.status = ProcessingStatus.PROCESSING
65
+ self.started_at = datetime.now(tz=timezone)
66
+
67
+ def failed(
68
+ self,
69
+ error_message: Optional[str] = None,
70
+ timezone: tzinfo = la_paz_tz,
71
+ ):
72
+ self.status = ProcessingStatus.FAILED
73
+ self.failed_at = datetime.now(tz=timezone)
74
+
75
+ def completed(self, timezone: tzinfo = la_paz_tz):
76
+ self.status = ProcessingStatus.COMPLETED
77
+ self.completed_at = datetime.now(tz=timezone)
78
+
79
+ def incomplete(self, timezone: tzinfo = la_paz_tz):
80
+ self.status = ProcessingStatus.INCOMPLETE
81
+ self.completed_at = datetime.now(tz=timezone)
82
+
83
+ def deleted(self):
84
+ self.status = ProcessingStatus.DELETED
85
+
86
+ def in_review(self):
87
+ self.status = ProcessingStatus.IN_REVIEW
88
+
89
+ def overload(
90
+ self,
91
+ new_instance: 'ProcessingCaseItem',
92
+ properties: List[str] = None,
93
+ ):
94
+ instance_properties = properties or [
95
+ 'status',
96
+ 'name',
97
+ 'document',
98
+ 'document_type',
99
+ 'uploaded_from',
100
+ 'processed_csv',
101
+ 'processed_xlsx',
102
+ 'processed_json',
103
+ 'processing_time',
104
+ 'processing_confidence',
105
+ 'uploaded_at',
106
+ 'started_at',
107
+ 'failed_at',
108
+ 'completed_at',
109
+ 'feedback',
110
+ 'metadata',
111
+ ]
112
+ for _property in instance_properties:
113
+ property_value = getattr(new_instance, _property)
114
+ if not hasattr(self, _property):
115
+ continue
116
+ setattr(self, _property, property_value)
117
+ return self
118
+
119
+ @property
120
+ def combined_id(self) -> str:
121
+ return f"{self.case_id}__{self.uuid}"
122
+
123
+ @property
124
+ def has_processed_csv(self) -> bool:
125
+ return self.processed_csv and self.processed_csv.is_valid
126
+
127
+ @property
128
+ def has_processed_xlsx(self) -> bool:
129
+ return self.processed_xlsx and self.processed_xlsx.is_valid
130
+
131
+ @property
132
+ def has_processed_json(self) -> bool:
133
+ return self.processed_json and self.processed_json.is_valid
134
+
135
+ @property
136
+ def is_procesable(self) -> bool:
137
+ return (
138
+ (self.status.is_pending or self.status.is_enqueued)
139
+ and self.digest
140
+ and self.document
141
+ and self.document.is_procesable
142
+ )
143
+
144
+ @property
145
+ def is_finished(self) -> bool:
146
+ return self.status in [
147
+ ProcessingStatus.COMPLETED,
148
+ ProcessingStatus.FAILED,
149
+ ]
150
+
151
+ @property
152
+ def to_dict(self) -> dict:
153
+ return {
154
+ 'uuid': self.uuid,
155
+ 'case_id': self.case_id,
156
+ 'digest': self.digest,
157
+ 'status': str(self.status),
158
+ 'name': self.name,
159
+ 'document':(
160
+ self.document.to_dict
161
+ if self.document else None
162
+ ),
163
+ 'document_type': (
164
+ str(self.document_type)
165
+ if self.document_type else None
166
+ ),
167
+ 'uploaded_from': (
168
+ str(self.uploaded_from)
169
+ if self.uploaded_from else None
170
+ ),
171
+ 'processed_csv': (
172
+ self.processed_csv.to_dict
173
+ if self.processed_csv else None
174
+ ),
175
+ 'processed_xlsx': (
176
+ self.processed_xlsx.to_dict
177
+ if self.processed_xlsx else None
178
+ ),
179
+ 'processed_json': (
180
+ self.processed_json.to_dict
181
+ if self.processed_json else None
182
+ ),
183
+ 'processing_time': (
184
+ normalize_number(self.processing_time)
185
+ if self.processing_time else None
186
+ ),
187
+ 'processing_confidence': (
188
+ normalize_number(self.processing_confidence)
189
+ if self.processing_confidence else None
190
+ ),
191
+ 'uploaded_at': (
192
+ self.uploaded_at.isoformat()
193
+ if self.uploaded_at else None
194
+ ),
195
+ 'started_at': (
196
+ self.started_at.isoformat()
197
+ if self.started_at else None
198
+ ),
199
+ 'failed_at': (
200
+ self.failed_at.isoformat()
201
+ if self.failed_at else None
202
+ ),
203
+ 'feedback': self.feedback,
204
+ 'metadata': self.metadata,
205
+ 'completed_at': (
206
+ self.completed_at.isoformat()
207
+ if self.completed_at else None
208
+ ),
209
+ }
210
+
211
+ @property
212
+ def to_simple_dict(self) -> dict:
213
+ simple_dict = self.to_dict.copy()
214
+ return simple_dict
215
+
216
+ @property
217
+ def to_queue_dict(self) -> dict:
218
+ queue_dict = self.to_dict.copy()
219
+ queue_dict["document"] = (
220
+ self.document.to_queue_dict
221
+ if self.document else None
222
+ )
223
+ queue_dict["processed_csv"] = (
224
+ self.processed_csv.to_queue_dict
225
+ if self.processed_csv else None
226
+ )
227
+ queue_dict["processed_xlsx"] = (
228
+ self.processed_xlsx.to_queue_dict
229
+ if self.processed_xlsx else None
230
+ )
231
+ queue_dict["processed_json"] = (
232
+ self.processed_json.to_queue_dict
233
+ if self.processed_json else None
234
+ )
235
+ return queue_dict
236
+
237
+ @property
238
+ def to_persist_dict(self) -> dict:
239
+ return self.to_dict
240
+
241
+ @classmethod
242
+ def from_dict(cls, data: dict) -> 'ProcessingCaseItem':
243
+ return cls(
244
+ uuid=data.get('uuid'),
245
+ case_id=data.get('case_id'),
246
+ digest=data.get('digest'),
247
+ status=ProcessingStatus.from_value(data.get('status')),
248
+ name=data.get('name'),
249
+ document=(
250
+ InMemoryDocument.from_dict(data.get('document'))
251
+ if data.get('document') else None
252
+ ),
253
+ document_type=(
254
+ ProcessingDocumentType.from_value(data.get('document_type'))
255
+ if data.get('document_type') else None
256
+ ),
257
+ uploaded_from=(
258
+ ProcessingSource.from_value(data.get('uploaded_from'))
259
+ if data.get('uploaded_from') else None
260
+ ),
261
+ processed_csv=(
262
+ InMemoryDocument.from_dict(data.get('processed_csv'))
263
+ if data.get('processed_csv') else None
264
+ ),
265
+ processed_xlsx=(
266
+ InMemoryDocument.from_dict(data.get('processed_xlsx'))
267
+ if data.get('processed_xlsx') else None
268
+ ),
269
+ processed_json=(
270
+ InMemoryDocument.from_dict(data.get('processed_json'))
271
+ if data.get('processed_json') else None
272
+ ),
273
+ processing_time=(
274
+ Decimal(data.get('processing_time'))
275
+ if data.get('processing_time') else None
276
+ ),
277
+ processing_confidence=(
278
+ Decimal(data.get('processing_confidence'))
279
+ if data.get('processing_confidence') else None
280
+ ),
281
+ uploaded_at=get_datetime_from_data(input_datetime=data.get('uploaded_at')),
282
+ started_at=get_datetime_from_data(input_datetime=data.get('started_at')),
283
+ failed_at=get_datetime_from_data(input_datetime=data.get('failed_at')),
284
+ feedback=data.get('feedback'),
285
+ metadata=data.get('metadata', {}),
286
+ completed_at=get_datetime_from_data(input_datetime=data.get('completed_at')),
287
+ )
288
+
289
+ @classmethod
290
+ def from_persist_dict(cls, data: dict) -> 'ProcessingCaseItem':
291
+ instance = cls.from_dict(data)
292
+ if "document_path" in data:
293
+ instance.document = InMemoryDocument(file_path=data["document_path"])
294
+ if "processed_csv_path" in data:
295
+ instance.processed_csv = InMemoryDocument(file_path=data["processed_csv_path"])
296
+ if "processed_xlsx_path" in data:
297
+ instance.processed_xlsx = InMemoryDocument(file_path=data["processed_xlsx_path"])
298
+ if "processed_json_path" in data:
299
+ instance.processed_json = InMemoryDocument(file_path=data["processed_json_path"])
300
+ return instance
@@ -0,0 +1,54 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime
3
+ from typing import List, Optional
4
+
5
+ from documente_shared.application.query_params import QueryParams
6
+ from documente_shared.domain.enums.common import ProcessingStatus
7
+ from documente_shared.domain.enums.document import (
8
+ DocumentProcessingStatus,
9
+ )
10
+ from documente_shared.domain.enums.processing_case import ProcessingDocumentType
11
+
12
+
13
+ @dataclass
14
+ class ProcessingCaseItemFilters(object):
15
+ sort_order: Optional[str] = None
16
+ search: Optional[str] = None
17
+ init_date: Optional[datetime] = None
18
+ end_date: Optional[datetime]= None
19
+ case_id: Optional[str] = None
20
+ statuses: List[ProcessingStatus] = None
21
+ document_types: List[ProcessingDocumentType] = None
22
+ include_archived: bool = False
23
+ tenant_slug: Optional[str] = None
24
+
25
+ def __post_init__(self):
26
+ self.statuses = self.statuses or []
27
+ self.document_types = self.document_types or []
28
+ self.sort_order = self.sort_order or "desc"
29
+
30
+
31
+ @classmethod
32
+ def from_params(cls, params: QueryParams) -> "ProcessingCaseItemFilters":
33
+ search_term = params.get_str(key="search", default=None)
34
+ return cls(
35
+ sort_order=params.get(key="sort", default="desc"),
36
+ search=search_term.strip() if search_term else None,
37
+ init_date=params.get_datetime(key="init_date", default=None),
38
+ end_date=params.get_datetime(key="end_date", default=None),
39
+ case_id=params.get_str(key="case_id", default=None),
40
+ statuses=params.get_enum_list(
41
+ key="statuses",
42
+ enum_class=DocumentProcessingStatus,
43
+ default=None,
44
+ ),
45
+ document_types=params.get_enum_list(
46
+ key="document_types",
47
+ enum_class=ProcessingDocumentType,
48
+ default=None,
49
+ ),
50
+ include_archived=params.get_bool(
51
+ key="include_archived",
52
+ default=False,
53
+ ),
54
+ )
@@ -0,0 +1,11 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+
4
+ from documente_shared.domain.entities.in_memory_document import InMemoryDocument
5
+
6
+
7
+ @dataclass
8
+ class ProcessedDocuments(object):
9
+ processed_csv: Optional[InMemoryDocument] = None
10
+ processed_xlsx: Optional[InMemoryDocument] = None
11
+ processed_json: Optional[InMemoryDocument] = None