elody 0.0.220__py3-none-any.whl → 0.0.222__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.
elody/job.py CHANGED
@@ -1,9 +1,11 @@
1
- from elody.object_configurations.job_configuration import JobConfiguration
2
- from datetime import datetime, timezone
1
+ try:
2
+ from config import get_object_configuration_mapper
3
3
 
4
- _config = JobConfiguration()
5
- _create = _config.crud()["creator"]
6
- _post_crud_hook = _config.crud()["post_crud_hook"]
4
+ _config = get_object_configuration_mapper().get("job")
5
+ except ModuleNotFoundError:
6
+ from elody.object_configurations.job_configuration import JobConfiguration
7
+
8
+ _config = JobConfiguration()
7
9
 
8
10
 
9
11
  def add_document_to_job(
@@ -12,18 +14,11 @@ def add_document_to_job(
12
14
  *,
13
15
  get_rabbit,
14
16
  ):
15
- relations = []
16
- if id_of_document_job_was_initiated_for:
17
- relations.append(
18
- {"key": id_of_document_job_was_initiated_for, "type": "isJobFor"}
19
- )
20
- document = {
21
- "id": id,
22
- "patch": {
23
- "relations": (relations),
24
- },
25
- }
26
- _post_crud_hook(crud="update", document=document, get_rabbit=get_rabbit)
17
+ _config.crud()["add_document_to_job"](
18
+ id=id,
19
+ id_of_document_job_was_initiated_for=id_of_document_job_was_initiated_for,
20
+ get_rabbit=get_rabbit,
21
+ )
27
22
 
28
23
 
29
24
  def init_job(
@@ -35,76 +30,57 @@ def init_job(
35
30
  user_email=None,
36
31
  parent_id=None,
37
32
  id_of_document_job_was_initiated_for=None,
33
+ track_async_children: bool | None = None,
38
34
  ) -> str:
39
- relations = []
40
- if parent_id:
41
- relations.append({"key": parent_id, "type": "hasParentJob"})
42
- if id_of_document_job_was_initiated_for:
43
- relations.append(
44
- {"key": id_of_document_job_was_initiated_for, "type": "isJobFor"}
45
- )
46
-
47
- job = _create(
48
- {
49
- "metadata": [
50
- {"key": "name", "value": name},
51
- {"key": "status", "value": "queued"},
52
- {"key": "type", "value": job_type},
53
- ],
54
- "relations": relations,
55
- "type": "job",
56
- },
57
- get_user_context=get_user_context
58
- or (lambda: type("UserContext", (object,), {"email": user_email})()),
35
+ return _config.crud()["init_job"](
36
+ name=name,
37
+ job_type=job_type,
38
+ get_rabbit=get_rabbit,
39
+ get_user_context=get_user_context,
40
+ user_email=user_email,
41
+ parent_id=parent_id,
42
+ id_of_document_job_was_initiated_for=id_of_document_job_was_initiated_for,
43
+ track_async_children=track_async_children,
59
44
  )
60
45
 
61
- _post_crud_hook(
62
- crud="create", document=job, parent_id=parent_id, get_rabbit=get_rabbit
63
- )
64
- return job["_id"]
65
-
66
46
 
67
47
  def start_job(
68
48
  id,
69
- id_of_document_job_was_initiated_for=None,
70
49
  *,
71
50
  get_rabbit,
72
51
  ):
73
- document = {
74
- "id": id,
75
- "patch": {
76
- "started_at": datetime.now(timezone.utc),
77
- "metadata": [{"key": "status", "value": "running"}],
78
- "relations": ([] if id_of_document_job_was_initiated_for else []),
79
- },
80
- }
81
- _post_crud_hook(crud="update", document=document, get_rabbit=get_rabbit)
52
+ _config.crud()["start_job"](
53
+ id=id,
54
+ get_rabbit=get_rabbit,
55
+ )
82
56
 
83
57
 
84
58
  def finish_job(
85
59
  id,
86
- id_of_document_job_was_initiated_for=None,
87
60
  *,
88
61
  get_rabbit,
89
62
  ):
90
- document = {
91
- "id": id,
92
- "patch": {
93
- "metadata": [{"key": "status", "value": "finished"}],
94
- "relations": ([] if id_of_document_job_was_initiated_for else []),
95
- },
96
- }
97
- _post_crud_hook(crud="update", document=document, get_rabbit=get_rabbit)
63
+ _config.crud()["finish_job"](
64
+ id=id,
65
+ get_rabbit=get_rabbit,
66
+ )
67
+
68
+
69
+ def soft_finish_job(
70
+ id,
71
+ *,
72
+ get_rabbit,
73
+ ):
74
+ _config.crud()["soft_finish_job"](id=id, get_rabbit=get_rabbit)
98
75
 
99
76
 
100
77
  def fail_job(id, exception_message, *, get_rabbit):
101
- document = {
102
- "id": id,
103
- "patch": {
104
- "metadata": [
105
- {"key": "info", "value": exception_message},
106
- {"key": "status", "value": "failed"},
107
- ]
108
- },
109
- }
110
- _post_crud_hook(crud="update", document=document, get_rabbit=get_rabbit)
78
+ _config.crud()["fail_job"](
79
+ id=id, exception_message=exception_message, get_rabbit=get_rabbit
80
+ )
81
+
82
+
83
+ def handle_parent_job_finished(id, parent_child_status, *, get_rabbit):
84
+ _config.crud()["handle_parent_job_finished"](
85
+ id=id, parent_child_status=parent_child_status, get_rabbit=get_rabbit
86
+ )
@@ -1,6 +1,7 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from copy import deepcopy
3
3
  from elody.migration.base_object_migrator import BaseObjectMigrator
4
+ from os import getenv
4
5
 
5
6
 
6
7
  class BaseObjectConfiguration(ABC):
@@ -95,6 +96,16 @@ class BaseObjectConfiguration(ABC):
95
96
  except Exception:
96
97
  return None
97
98
 
99
+ def _is_request_from_internal_service(self):
100
+ try:
101
+ from flask import request # pyright: ignore
102
+
103
+ return request.headers.get("Authorization", "").removeprefix(
104
+ "Bearer "
105
+ ) == getenv("STATIC_JWT")
106
+ except Exception:
107
+ return False
108
+
98
109
  def _sanitize_document(self, *, document, **kwargs):
99
110
  sanitized_document = {}
100
111
  document_deepcopy = deepcopy(document)
@@ -177,6 +177,9 @@ class ElodyConfiguration(BaseObjectConfiguration):
177
177
  return sanitized_document
178
178
 
179
179
  def __patch_document_audit_info(self, crud, document, timestamp, audit_override):
180
+ if self._is_request_from_internal_service():
181
+ return document
182
+
180
183
  document.update({f"date_{crud}d": timestamp})
181
184
  if email := self._get_user_context_id():
182
185
  label = f"{crud}d_by" if crud == "create" else "last_editor"
@@ -1,8 +1,20 @@
1
+ from datetime import datetime, timezone
2
+ from enum import Enum
3
+ from os import getenv
4
+ from typing import Literal
5
+
1
6
  from elody.object_configurations.elody_configuration import (
2
7
  ElodyConfiguration,
3
8
  )
4
9
  from elody.util import send_cloudevent
5
- from os import getenv
10
+
11
+
12
+ class Status(str, Enum):
13
+ QUEUED = "queued"
14
+ RUNNING = "running"
15
+ FINISHED = "finished"
16
+ FAILED = "failed"
17
+ WARNING = "warning"
6
18
 
7
19
 
8
20
  class JobConfiguration(ElodyConfiguration):
@@ -10,7 +22,23 @@ class JobConfiguration(ElodyConfiguration):
10
22
  SCHEMA_VERSION = 1
11
23
 
12
24
  def crud(self):
13
- crud = {"collection": "jobs", "collection_history": ""}
25
+ crud = {
26
+ "collection": "jobs",
27
+ "collection_history": "",
28
+ "add_document_to_job": lambda *args, **kwargs: self._add_document_to_job(
29
+ *args, **kwargs
30
+ ),
31
+ "init_job": lambda *args, **kwargs: self._init_job(*args, **kwargs),
32
+ "start_job": lambda *args, **kwargs: self._start_job(*args, **kwargs),
33
+ "finish_job": lambda *args, **kwargs: self._finish_job(*args, **kwargs),
34
+ "finish_job_with_warning": lambda *args, **kwargs: self._finish_job_with_warning(
35
+ *args, **kwargs
36
+ ),
37
+ "fail_job": lambda *args, **kwargs: self._fail_job(*args, **kwargs),
38
+ "handle_parent_job_finished": lambda *args, **kwargs: self._handle_parent_job_finished(
39
+ *args, **kwargs
40
+ ),
41
+ }
14
42
  return {**super().crud(), **crud}
15
43
 
16
44
  def document_info(self):
@@ -49,3 +77,169 @@ class JobConfiguration(ElodyConfiguration):
49
77
  "dams.job_changed",
50
78
  document,
51
79
  )
80
+
81
+ def _add_document_to_job(
82
+ self,
83
+ id,
84
+ id_of_document_job_was_initiated_for,
85
+ *,
86
+ get_rabbit,
87
+ ):
88
+ relations = []
89
+ if id_of_document_job_was_initiated_for:
90
+ relations.append(
91
+ {"key": id_of_document_job_was_initiated_for, "type": "isJobFor"}
92
+ )
93
+ document = {
94
+ "id": id,
95
+ "patch": {
96
+ "relations": (relations),
97
+ },
98
+ }
99
+ self._post_crud_hook(crud="update", document=document, get_rabbit=get_rabbit)
100
+
101
+ def _init_job(
102
+ self,
103
+ name,
104
+ job_type,
105
+ *,
106
+ get_rabbit,
107
+ get_user_context=None,
108
+ user_email=None,
109
+ parent_id=None,
110
+ id_of_document_job_was_initiated_for=None,
111
+ track_async_children: bool | None = None,
112
+ ) -> str:
113
+ relations = []
114
+ if parent_id:
115
+ relations.append({"key": parent_id, "type": "hasParentJob"})
116
+ if id_of_document_job_was_initiated_for:
117
+ relations.append(
118
+ {"key": id_of_document_job_was_initiated_for, "type": "isJobFor"}
119
+ )
120
+
121
+ metadata = [
122
+ {"key": "name", "value": name},
123
+ {"key": "status", "value": Status.QUEUED.value},
124
+ {"key": "type", "value": job_type},
125
+ ]
126
+
127
+ if track_async_children:
128
+ metadata.append(
129
+ {
130
+ "key": "child_jobs",
131
+ "value": {
132
+ Status.QUEUED.value: 0,
133
+ Status.RUNNING.value: 0,
134
+ Status.FAILED.value: 0,
135
+ Status.FINISHED.value: 0,
136
+ Status.WARNING.value: 0,
137
+ },
138
+ }
139
+ )
140
+
141
+ job = self.crud()["creator"](
142
+ {
143
+ "metadata": metadata,
144
+ "relations": relations,
145
+ "type": "job",
146
+ },
147
+ get_user_context=get_user_context
148
+ or (lambda: type("UserContext", (object,), {"email": user_email})()),
149
+ )
150
+
151
+ self._post_crud_hook(
152
+ crud="create", document=job, parent_id=parent_id, get_rabbit=get_rabbit
153
+ )
154
+ return job["_id"]
155
+
156
+ def _start_job(
157
+ self,
158
+ id,
159
+ *,
160
+ get_rabbit,
161
+ ):
162
+ document = {
163
+ "id": id,
164
+ "patch": {
165
+ "started_at": datetime.now(timezone.utc),
166
+ "metadata": [{"key": "status", "value": Status.RUNNING.value}],
167
+ },
168
+ }
169
+ self._post_crud_hook(crud="update", document=document, get_rabbit=get_rabbit)
170
+
171
+ def _finish_job(
172
+ self,
173
+ id,
174
+ *,
175
+ get_rabbit,
176
+ ):
177
+ document = {
178
+ "id": id,
179
+ "patch": {
180
+ "metadata": [{"key": "status", "value": Status.FINISHED.value}],
181
+ },
182
+ }
183
+ self._post_crud_hook(crud="update", document=document, get_rabbit=get_rabbit)
184
+
185
+ def _finish_job_with_warning(
186
+ self,
187
+ id,
188
+ *,
189
+ get_rabbit,
190
+ info_message=None,
191
+ ):
192
+ document = {
193
+ "id": id,
194
+ "patch": {
195
+ "metadata": [
196
+ {"key": "status", "value": Status.FINISHED.value},
197
+ {"key": "info", "value": info_message},
198
+ ],
199
+ },
200
+ }
201
+ self._post_crud_hook(crud="update", document=document, get_rabbit=get_rabbit)
202
+
203
+ def _fail_job(self, id, exception_message, *, get_rabbit):
204
+
205
+ status, message = self.__handle_error_warnings(exception_message)
206
+
207
+ document = {
208
+ "id": id,
209
+ "patch": {
210
+ "metadata": [
211
+ {"key": "info", "value": message},
212
+ {
213
+ "key": "status",
214
+ "value": (status.value),
215
+ },
216
+ ]
217
+ },
218
+ }
219
+ self._post_crud_hook(crud="update", document=document, get_rabbit=get_rabbit)
220
+
221
+ def _handle_parent_job_finished(self, id, parent_child_status, *, get_rabbit):
222
+
223
+ child_jobs_failed = parent_child_status[Status.FAILED.value]
224
+ child_jobs_warning = parent_child_status[Status.WARNING.value]
225
+
226
+ if child_jobs_failed == 0:
227
+ if child_jobs_warning:
228
+ self.crud()["finish_job_with_warning"](
229
+ id, get_rabbit=get_rabbit, info_message="Some subjobs have warnings"
230
+ )
231
+ self.crud()["finish_job"](id, get_rabbit=get_rabbit)
232
+ else:
233
+ self.crud()["fail_job"](
234
+ id, exception_message="some subjobs failed", get_rabbit=get_rabbit
235
+ )
236
+
237
+ def __handle_error_warnings(
238
+ self,
239
+ errormessage: str,
240
+ ) -> tuple[Literal[Status.WARNING] | Literal[Status.FAILED], str]:
241
+
242
+ if errormessage.startswith("W4009"):
243
+ return Status.WARNING, errormessage
244
+
245
+ return Status.FAILED, errormessage
elody/util.py CHANGED
@@ -125,6 +125,13 @@ def get_item_metadata_value(item, key):
125
125
  return ""
126
126
 
127
127
 
128
+ def get_item_relation_key(item, type):
129
+ for item in item.get("relations", []):
130
+ if item["type"] == type:
131
+ return item["key"]
132
+ return ""
133
+
134
+
128
135
  def get_mimetype_from_filename(filename):
129
136
  mime = mimetypes.guess_type(filename, False)[0]
130
137
  return mime if mime else "application/octet-stream"
@@ -267,8 +274,12 @@ def signal_update_copyright_color_mediafile(mq_client, mediafile_id):
267
274
  send_cloudevent(mq_client, "dams", "dams.update_copyright_color_mediafile", data)
268
275
 
269
276
 
270
- def signal_upload_file(mq_client, upload_links, selected_folder):
271
- data = {"upload_links": upload_links, "selected_folder": selected_folder}
277
+ def signal_upload_file(mq_client, upload_links, selected_folder, parent_job_id=None):
278
+ data = {
279
+ "upload_links": upload_links,
280
+ "selected_folder": selected_folder,
281
+ "parent_job_id": parent_job_id,
282
+ }
272
283
  send_cloudevent(mq_client, "dams", "dams.upload_file", data)
273
284
 
274
285
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: elody
3
- Version: 0.0.220
3
+ Version: 0.0.222
4
4
  Summary: elody SDK for Python
5
5
  Author-email: Inuits <developers@inuits.eu>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -4,17 +4,17 @@ elody/client.py,sha256=15SBfnLHJXXY5Vf5BnkWdjtvkH21E_AsWTzm2-zcbf0,8799
4
4
  elody/csv.py,sha256=f8HphE-KC2OqKFzV0HiifWBgMHb3g0EA_Y82o_6JEiE,16761
5
5
  elody/error_codes.py,sha256=d3wNPZU0HFMyl8xN95ciNd6QD94al1tX18h6HiqiAHo,4310
6
6
  elody/exceptions.py,sha256=5KSw2sPCZz3lDIJX4LiR2iL9n4m4KIil04D1d3X5rd0,968
7
- elody/job.py,sha256=4dNpTWDdui53VqXfPsDNoTZ9MQohfqXnT2AsuI8q5Ec,2900
7
+ elody/job.py,sha256=EGfYozNxyx2iLC-j_L5XLzV3hUAOM9UIFuYzyqKpS1w,1908
8
8
  elody/loader.py,sha256=yNakab07ja478gZaIC277n8Wf8SJ7_P3q-Z39BDdWQc,5419
9
9
  elody/schemas.py,sha256=WtKdZEAX-PtEuAaRohyS3Md8H4-8yKVXMkHfCQ2SDR4,4676
10
- elody/util.py,sha256=QqcqkV7GZ_1p4Uf_GJnc_nfAJt0mkBGzQ7-wCxMJ1ZM,9080
10
+ elody/util.py,sha256=Qcoe49DsoJwXLFNevR67U_04iuJkmX4Pl0bcJ0Alrh0,9325
11
11
  elody/validator.py,sha256=G7Ya538EJHCFzOxEri2OcFMabfLBCtTKxuf4os_KuNw,260
12
12
  elody/migration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  elody/migration/base_object_migrator.py,sha256=n8uvgGfjEUy60G47RD7Y-oxp1vHLOauwPMDl87LcxtU,436
14
14
  elody/object_configurations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- elody/object_configurations/base_object_configuration.py,sha256=AqOE0hCbXQDOdKFnDa9dbi-A-GRTUAaBZUe6_g0-Dc0,7573
16
- elody/object_configurations/elody_configuration.py,sha256=T_gyQykQWC1wdGTwQ9DujmumG8uqgQlH0vbZKmxtpfw,11049
17
- elody/object_configurations/job_configuration.py,sha256=-dGhXGfCewLEcPLjkBiSgwUa1dPty6OMm8b0IOUOUgo,1507
15
+ elody/object_configurations/base_object_configuration.py,sha256=4lk6BmhRSYxwuD2qqGfE8d9H9ak7iwoOM5gqw7EntTI,7905
16
+ elody/object_configurations/elody_configuration.py,sha256=Gf_59TfqjvlkufnkfzTtE4p8cBly613ieJ5beemtv00,11131
17
+ elody/object_configurations/job_configuration.py,sha256=T6QI2wE-u7ZcNpYE5XjmrCzIJglyKUGJsifaTn0WGyg,7512
18
18
  elody/object_configurations/saved_search_configuration.py,sha256=ddOry4EqYOeEKRF7q2M_fHoqZv8DXpQjFq8VaZ7jhVI,732
19
19
  elody/policies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  elody/policies/helpers.py,sha256=0mlTd-hu2TKwVNH3ov4yCEV-P_f2k7XFJ7XYUtGTjXk,2239
@@ -35,13 +35,13 @@ elody/policies/authorization/mediafile_derivatives_policy.py,sha256=OwNpbS8i7-Lz
35
35
  elody/policies/authorization/mediafile_download_policy.py,sha256=XMsKavBucmTh4W1kWOzpFWxJ_ZXgHVK1RS7JB4HjtQo,1979
36
36
  elody/policies/authorization/multi_tenant_policy.py,sha256=SA9H7SBjzuh8mY3gYN7pDG8TV7hdI3GEUtNeiZeNL3M,3164
37
37
  elody/policies/authorization/tenant_request_policy.py,sha256=dEgblwRAqwWVcE-O7Jn8hVL3OnwDlQhDEOcPlcElBrk,1185
38
- elody-0.0.220.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
38
+ elody-0.0.222.dist-info/licenses/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092
39
39
  tests/__init_.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
40
  tests/data.py,sha256=Q3oxduf-E3m-Z5G_p3fcs8jVy6g10I7zXKL1m94UVMI,2906
41
41
  tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  tests/unit/test_csv.py,sha256=NQaOhehfQ4GuXku0Y1SA8DYjJeqqidbF50zEHAi8RZA,15923
43
43
  tests/unit/test_utils.py,sha256=g63szcEZyHhCOtrW4BnNbcgVca3oYPIOLjBdIzNwwN0,8784
44
- elody-0.0.220.dist-info/METADATA,sha256=LQldo9-kZVg9Pv6vWVM-zx0O7jQl5sGO-BQ6Wpj_LPE,23358
45
- elody-0.0.220.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
46
- elody-0.0.220.dist-info/top_level.txt,sha256=E0mImupLj0KmtUUCXRYEoLDRaSkuiGaOIIseAa0oQ-M,21
47
- elody-0.0.220.dist-info/RECORD,,
44
+ elody-0.0.222.dist-info/METADATA,sha256=N8xZrzgeNCwpRTo8WLIlxfFE9ZYEegaEJIloyTDXlHw,23358
45
+ elody-0.0.222.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
46
+ elody-0.0.222.dist-info/top_level.txt,sha256=E0mImupLj0KmtUUCXRYEoLDRaSkuiGaOIIseAa0oQ-M,21
47
+ elody-0.0.222.dist-info/RECORD,,