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.
- documente_shared/__init__.py +0 -0
- documente_shared/application/__init__.py +0 -0
- documente_shared/application/dates.py +7 -0
- documente_shared/application/digest.py +7 -0
- documente_shared/application/exceptions.py +23 -0
- documente_shared/application/files.py +27 -0
- documente_shared/application/json.py +45 -0
- documente_shared/application/numbers.py +7 -0
- documente_shared/application/payloads.py +29 -0
- documente_shared/application/query_params.py +133 -0
- documente_shared/application/retry_utils.py +69 -0
- documente_shared/application/time_utils.py +13 -0
- documente_shared/application/timezone.py +7 -0
- documente_shared/domain/__init__.py +0 -0
- documente_shared/domain/base_enum.py +54 -0
- documente_shared/domain/constants.py +8 -0
- documente_shared/domain/entities/__init__.py +0 -0
- documente_shared/domain/entities/document.py +410 -0
- documente_shared/domain/entities/document_metadata.py +64 -0
- documente_shared/domain/entities/in_memory_document.py +75 -0
- documente_shared/domain/entities/processing_case.py +215 -0
- documente_shared/domain/entities/processing_case_filters.py +51 -0
- documente_shared/domain/entities/processing_case_item.py +300 -0
- documente_shared/domain/entities/processing_case_item_filters.py +54 -0
- documente_shared/domain/entities/processing_documents.py +11 -0
- documente_shared/domain/entities/processing_event.py +71 -0
- documente_shared/domain/entities/scaling.py +31 -0
- documente_shared/domain/enums/__init__.py +0 -0
- documente_shared/domain/enums/circular_oficio.py +29 -0
- documente_shared/domain/enums/common.py +133 -0
- documente_shared/domain/enums/document.py +124 -0
- documente_shared/domain/enums/document_type_record.py +13 -0
- documente_shared/domain/enums/processing_case.py +66 -0
- documente_shared/domain/exceptions.py +5 -0
- documente_shared/domain/interfaces/__init__.py +0 -0
- documente_shared/domain/interfaces/scaling.py +10 -0
- documente_shared/domain/repositories/__init__.py +0 -0
- documente_shared/domain/repositories/document.py +24 -0
- documente_shared/domain/repositories/processing_case.py +36 -0
- documente_shared/domain/repositories/processing_case_item.py +49 -0
- documente_shared/infrastructure/__init__.py +0 -0
- documente_shared/infrastructure/documente_client.py +27 -0
- documente_shared/infrastructure/dynamo_table.py +75 -0
- documente_shared/infrastructure/lambdas.py +14 -0
- documente_shared/infrastructure/repositories/__init__.py +0 -0
- documente_shared/infrastructure/repositories/dynamo_document.py +43 -0
- documente_shared/infrastructure/repositories/dynamo_processing_case.py +55 -0
- documente_shared/infrastructure/repositories/dynamo_processing_case_item.py +70 -0
- documente_shared/infrastructure/repositories/http_document.py +66 -0
- documente_shared/infrastructure/repositories/http_processing_case.py +82 -0
- documente_shared/infrastructure/repositories/http_processing_case_item.py +118 -0
- documente_shared/infrastructure/repositories/mem_document.py +46 -0
- documente_shared/infrastructure/repositories/mem_processing_case.py +44 -0
- documente_shared/infrastructure/repositories/mem_processing_case_item.py +52 -0
- documente_shared/infrastructure/s3_bucket.py +58 -0
- documente_shared/infrastructure/services/__init__.py +0 -0
- documente_shared/infrastructure/services/http_scaling.py +25 -0
- documente_shared/infrastructure/sqs_queue.py +48 -0
- documente_shared/presentation/__init__.py +0 -0
- documente_shared/presentation/presenters.py +16 -0
- documente_shared-0.1.145.dist-info/METADATA +39 -0
- documente_shared-0.1.145.dist-info/RECORD +63 -0
- 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
|