cledar-sdk 2.0.2__py3-none-any.whl → 2.0.3__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 (85) hide show
  1. cledar/__init__.py +0 -0
  2. cledar/kafka/README.md +239 -0
  3. cledar/kafka/__init__.py +40 -0
  4. cledar/kafka/clients/base.py +98 -0
  5. cledar/kafka/clients/consumer.py +110 -0
  6. cledar/kafka/clients/producer.py +80 -0
  7. cledar/kafka/config/schemas.py +178 -0
  8. cledar/kafka/exceptions.py +22 -0
  9. cledar/kafka/handlers/dead_letter.py +82 -0
  10. cledar/kafka/handlers/parser.py +49 -0
  11. cledar/kafka/logger.py +3 -0
  12. cledar/kafka/models/input.py +13 -0
  13. cledar/kafka/models/message.py +10 -0
  14. cledar/kafka/models/output.py +8 -0
  15. cledar/kafka/tests/.env.test.kafka +3 -0
  16. cledar/kafka/tests/README.md +216 -0
  17. cledar/kafka/tests/conftest.py +104 -0
  18. cledar/kafka/tests/integration/__init__.py +1 -0
  19. cledar/kafka/tests/integration/conftest.py +78 -0
  20. cledar/kafka/tests/integration/helpers.py +47 -0
  21. cledar/kafka/tests/integration/test_consumer_integration.py +375 -0
  22. cledar/kafka/tests/integration/test_integration.py +394 -0
  23. cledar/kafka/tests/integration/test_producer_consumer_interaction.py +388 -0
  24. cledar/kafka/tests/integration/test_producer_integration.py +217 -0
  25. cledar/kafka/tests/unit/__init__.py +1 -0
  26. cledar/kafka/tests/unit/test_base_kafka_client.py +391 -0
  27. cledar/kafka/tests/unit/test_config_validation.py +609 -0
  28. cledar/kafka/tests/unit/test_dead_letter_handler.py +443 -0
  29. cledar/kafka/tests/unit/test_error_handling.py +674 -0
  30. cledar/kafka/tests/unit/test_input_parser.py +310 -0
  31. cledar/kafka/tests/unit/test_input_parser_comprehensive.py +489 -0
  32. cledar/kafka/tests/unit/test_utils.py +25 -0
  33. cledar/kafka/tests/unit/test_utils_comprehensive.py +408 -0
  34. cledar/kafka/utils/callbacks.py +19 -0
  35. cledar/kafka/utils/messages.py +28 -0
  36. cledar/kafka/utils/topics.py +2 -0
  37. cledar/kserve/README.md +352 -0
  38. cledar/kserve/__init__.py +3 -0
  39. cledar/kserve/tests/__init__.py +0 -0
  40. cledar/kserve/tests/test_utils.py +64 -0
  41. cledar/kserve/utils.py +27 -0
  42. cledar/logging/README.md +53 -0
  43. cledar/logging/__init__.py +3 -0
  44. cledar/logging/tests/test_universal_plaintext_formatter.py +249 -0
  45. cledar/logging/universal_plaintext_formatter.py +94 -0
  46. cledar/monitoring/README.md +71 -0
  47. cledar/monitoring/__init__.py +3 -0
  48. cledar/monitoring/monitoring_server.py +112 -0
  49. cledar/monitoring/tests/integration/test_monitoring_server_int.py +162 -0
  50. cledar/monitoring/tests/test_monitoring_server.py +59 -0
  51. cledar/nonce/README.md +99 -0
  52. cledar/nonce/__init__.py +3 -0
  53. cledar/nonce/nonce_service.py +36 -0
  54. cledar/nonce/tests/__init__.py +0 -0
  55. cledar/nonce/tests/test_nonce_service.py +136 -0
  56. cledar/redis/README.md +536 -0
  57. cledar/redis/__init__.py +15 -0
  58. cledar/redis/async_example.py +111 -0
  59. cledar/redis/example.py +37 -0
  60. cledar/redis/exceptions.py +22 -0
  61. cledar/redis/logger.py +3 -0
  62. cledar/redis/model.py +10 -0
  63. cledar/redis/redis.py +525 -0
  64. cledar/redis/redis_config_store.py +252 -0
  65. cledar/redis/tests/test_async_integration_redis.py +158 -0
  66. cledar/redis/tests/test_async_redis_service.py +380 -0
  67. cledar/redis/tests/test_integration_redis.py +119 -0
  68. cledar/redis/tests/test_redis_service.py +319 -0
  69. cledar/storage/README.md +529 -0
  70. cledar/storage/__init__.py +4 -0
  71. cledar/storage/constants.py +3 -0
  72. cledar/storage/exceptions.py +50 -0
  73. cledar/storage/models.py +19 -0
  74. cledar/storage/object_storage.py +955 -0
  75. cledar/storage/tests/conftest.py +18 -0
  76. cledar/storage/tests/test_abfs.py +164 -0
  77. cledar/storage/tests/test_integration_filesystem.py +359 -0
  78. cledar/storage/tests/test_integration_s3.py +453 -0
  79. cledar/storage/tests/test_local.py +384 -0
  80. cledar/storage/tests/test_s3.py +521 -0
  81. {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.0.3.dist-info}/METADATA +1 -1
  82. cledar_sdk-2.0.3.dist-info/RECORD +84 -0
  83. cledar_sdk-2.0.2.dist-info/RECORD +0 -4
  84. {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.0.3.dist-info}/WHEEL +0 -0
  85. {cledar_sdk-2.0.2.dist-info → cledar_sdk-2.0.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,310 @@
1
+ from collections.abc import Sequence
2
+ from typing import Any, TypeVar
3
+
4
+ import pydantic
5
+ import pytest
6
+ from faker import Faker
7
+ from pydantic import BaseModel
8
+
9
+ from cledar.kafka.handlers.parser import (
10
+ IncorrectMessageValueError,
11
+ InputParser,
12
+ )
13
+ from cledar.kafka.models.message import KafkaMessage
14
+
15
+
16
+ @pydantic.dataclasses.dataclass
17
+ class S3Metadata:
18
+ url: str
19
+ type: str | None
20
+ container: str | None
21
+
22
+
23
+ @pydantic.dataclasses.dataclass
24
+ class ContentNetwork:
25
+ id: str | None
26
+ name: str
27
+
28
+
29
+ @pydantic.dataclasses.dataclass
30
+ class ContentDistributor:
31
+ id: str | None
32
+ name: str
33
+
34
+
35
+ @pydantic.dataclasses.dataclass
36
+ class ContentMetadata:
37
+ id: str | None
38
+ medium: str
39
+ type: str
40
+
41
+
42
+ @pydantic.dataclasses.dataclass
43
+ class ContentStream:
44
+ id: str
45
+ type: str | None
46
+ index: int | None
47
+ codec_name: str | None
48
+
49
+
50
+ @pydantic.dataclasses.dataclass
51
+ class AudioStreamInfo(ContentStream):
52
+ language: str | None
53
+ format: str | None
54
+ channels: int | None
55
+ channels_layout: str | None
56
+ sample_rate: int | None
57
+ bit_rate: int | None
58
+ frame_size: int | None
59
+ delay: int | None
60
+
61
+
62
+ @pydantic.dataclasses.dataclass
63
+ class VideoStreamInfo(ContentStream):
64
+ format: str | None
65
+ width: int | None
66
+ height: int | None
67
+ display_aspect_ratio: str | None
68
+ pixel_aspect_ratio: str | None
69
+ pixel_format: str | None
70
+ framerate: str | None
71
+ delay: int | None
72
+
73
+
74
+ @pydantic.dataclasses.dataclass
75
+ class SubtitleStreamInfo(ContentStream):
76
+ language: str | None
77
+ delay: int | None
78
+
79
+
80
+ @pydantic.dataclasses.dataclass
81
+ class ContentOrigin:
82
+ id: str
83
+ name: str
84
+ url: str
85
+ mode: str
86
+ target: str
87
+
88
+ network: ContentNetwork
89
+ distributor: ContentDistributor
90
+ metadata: ContentMetadata
91
+
92
+
93
+ @pydantic.dataclasses.dataclass
94
+ class PipelineMessageReference:
95
+ id: str
96
+ created_at: str
97
+ created_by: str
98
+
99
+
100
+ StreamInfo = AudioStreamInfo | VideoStreamInfo | SubtitleStreamInfo
101
+ PipelineMessageData = TypeVar("PipelineMessageData", bound=BaseModel)
102
+
103
+
104
+ @pydantic.dataclasses.dataclass
105
+ class PipelineMessageDataContainer[PipelineMessageData: BaseModel]:
106
+ data: dict[str, Any] | PipelineMessageData
107
+
108
+
109
+ @pydantic.dataclasses.dataclass
110
+ class PipelineStageReference[PipelineMessageData: BaseModel](
111
+ PipelineMessageReference,
112
+ PipelineMessageDataContainer[PipelineMessageData],
113
+ ):
114
+ output_topic_name: str
115
+ parent_ids: list[str]
116
+
117
+
118
+ @pydantic.dataclasses.dataclass
119
+ class PipelineMessageMetadata[PipelineMessageData: BaseModel]:
120
+ origin: ContentOrigin
121
+ streams: list[StreamInfo]
122
+ pipeline_stages: list[PipelineStageReference[PipelineMessageData]]
123
+
124
+
125
+ @pydantic.dataclasses.dataclass
126
+ class PipelineMessage[PipelineMessageData: BaseModel](
127
+ PipelineMessageReference,
128
+ PipelineMessageDataContainer[PipelineMessageData],
129
+ ):
130
+ metadata: PipelineMessageMetadata[PipelineMessageData]
131
+
132
+
133
+ class InputMessageData(BaseModel):
134
+ chunks: Sequence[S3Metadata]
135
+
136
+
137
+ class InputMessagePayload(BaseModel, PipelineMessage[InputMessageData]):
138
+ data: InputMessageData
139
+
140
+
141
+ fake = Faker()
142
+
143
+ input_json_value = '{"data":{"chunks":[{"url":"s3://reference-transformed-chunks/video/0285ee22c95f43f9a98792d210aa6813_stream_0.mp4","type":"video","container":"mpegts"},{"url":"s3://reference-transformed-chunks/audio/0285ee22c95f43f9a98792d210aa6813_stream_1.mp4","type":"audio","container":"mpegts"},{"url":"s3://reference-transformed-chunks/audio/0285ee22c95f43f9a98792d210aa6813_stream_2.mp4","type":"audio","container":"mpegts"},{"url":"s3://reference-transformed-chunks/audio/0285ee22c95f43f9a98792d210aa6813_stream_6.mp4","type":"audio","container":"mpegts"}]},"id":"78c7392659984c2d89057bd628275855","created_at":"2024-08-30T11:09:56Z","created_by":"chunk-transformer","metadata":{"origin":{"id":"dbd5f857572e49bfb8dd678dd14a4b85","name":"TVP_1","url":"https://example.com","mode":"broadcast","target":"television","network":{"id":null,"name":"korbank"},"distributor":{"id":null,"name":"TVP_1"},"metadata":{"id":null,"medium":"video","type":"channel"}},"streams":[{"id":"2cad60eac39f4cb4a676eb001350c3c3","type":"video","index":0,"codec_name":"h264","format":"yuv420p","width":1920,"height":1080,"display_aspect_ratio":"16:9","pixel_aspect_ratio":"1:1","pixel_format":"yuv420p","framerate":"25:1","delay":0},{"id":"9a8c7216c1e24f9eb2152c52ac3feb50","type":"audio","index":1,"codec_name":"mp2","language":"pol","format":"s16p","channels":2,"channels_layout":"stereo","sample_rate":48000,"bit_rate":192000,"frame_size":1152,"delay":0},{"id":"50bc1f29f4b84b28a92aa263567dd9ca","type":"audio","index":2,"codec_name":"ac3","language":"qaa","format":"fltp","channels":6,"channels_layout":"5.1(side)","sample_rate":48000,"bit_rate":384000,"frame_size":0,"delay":0},{"id":"eb2f0753258b4d0fbf77af82ac7cff59","type":"subtitle","index":4,"codec_name":"dvbsub","language":"pol","delay":0},{"id":"dc57b3f8fdb04420bc820fe1d45985b6","type":"audio","index":6,"codec_name":"mp2","language":"aux","format":"s16p","channels":2,"channels_layout":"stereo","sample_rate":48000,"bit_rate":128000,"frame_size":1152,"delay":0}],"pipeline_stages":[{"data":{},"id":"c577dd6d2be743d5a155ef75d6d1ec79","created_at":"2024-08-30T11:09:55Z","created_by":"stream-chunker","output_topic_name":"reference-chunks","parent_ids":[]},{"data":{"id":"f11a8990bfff4288b9b284cb9908c788","created_at":"2024-08-30T11:09:55Z","created_by":"stream-chunker","s3_key":"0285ee22c95f43f9a98792d210aa6813.mpegts","s3_bucket":"reference-chunks","chunk_duration":5,"chunk_overlap_sec":0,"chunk_size":5179024,"ignored_streams":[3,5,7]},"id":"78c7392659984c2d89057bd628275855","created_at":"2024-08-30T11:09:56Z","created_by":"chunk-transformer","output_topic_name":"reference-transformed-chunks","parent_ids":[]}]}}'
144
+
145
+ expected = InputMessagePayload( # type: ignore
146
+ id="78c7392659984c2d89057bd628275855",
147
+ created_at="2024-08-30T11:09:56Z",
148
+ created_by="chunk-transformer",
149
+ data=InputMessageData(
150
+ chunks=[
151
+ S3Metadata(
152
+ url="s3://reference-transformed-chunks/video/0285ee22c95f43f9a98792d210aa6813_stream_0.mp4",
153
+ type="video",
154
+ container="mpegts",
155
+ ),
156
+ S3Metadata(
157
+ url="s3://reference-transformed-chunks/audio/0285ee22c95f43f9a98792d210aa6813_stream_1.mp4",
158
+ type="audio",
159
+ container="mpegts",
160
+ ),
161
+ S3Metadata(
162
+ url="s3://reference-transformed-chunks/audio/0285ee22c95f43f9a98792d210aa6813_stream_2.mp4",
163
+ type="audio",
164
+ container="mpegts",
165
+ ),
166
+ S3Metadata(
167
+ url="s3://reference-transformed-chunks/audio/0285ee22c95f43f9a98792d210aa6813_stream_6.mp4",
168
+ type="audio",
169
+ container="mpegts",
170
+ ),
171
+ ],
172
+ ),
173
+ metadata=PipelineMessageMetadata(
174
+ origin=ContentOrigin(
175
+ id="dbd5f857572e49bfb8dd678dd14a4b85",
176
+ name="TVP_1",
177
+ url="https://example.com",
178
+ mode="broadcast",
179
+ target="television",
180
+ network=ContentNetwork(id=None, name="korbank"),
181
+ distributor=ContentDistributor(id=None, name="TVP_1"),
182
+ metadata=ContentMetadata(id=None, medium="video", type="channel"),
183
+ ),
184
+ streams=[
185
+ VideoStreamInfo(
186
+ id="2cad60eac39f4cb4a676eb001350c3c3",
187
+ type="video",
188
+ index=0,
189
+ codec_name="h264",
190
+ format="yuv420p",
191
+ width=1920,
192
+ height=1080,
193
+ display_aspect_ratio="16:9",
194
+ pixel_aspect_ratio="1:1",
195
+ pixel_format="yuv420p",
196
+ framerate="25:1",
197
+ delay=0,
198
+ ),
199
+ AudioStreamInfo(
200
+ id="9a8c7216c1e24f9eb2152c52ac3feb50",
201
+ type="audio",
202
+ index=1,
203
+ codec_name="mp2",
204
+ language="pol",
205
+ format="s16p",
206
+ channels=2,
207
+ channels_layout="stereo",
208
+ sample_rate=48000,
209
+ bit_rate=192000,
210
+ frame_size=1152,
211
+ delay=0,
212
+ ),
213
+ AudioStreamInfo(
214
+ id="50bc1f29f4b84b28a92aa263567dd9ca",
215
+ type="audio",
216
+ index=2,
217
+ codec_name="ac3",
218
+ language="qaa",
219
+ format="fltp",
220
+ channels=6,
221
+ channels_layout="5.1(side)",
222
+ sample_rate=48000,
223
+ bit_rate=384000,
224
+ frame_size=0,
225
+ delay=0,
226
+ ),
227
+ SubtitleStreamInfo(
228
+ id="eb2f0753258b4d0fbf77af82ac7cff59",
229
+ type="subtitle",
230
+ index=4,
231
+ codec_name="dvbsub",
232
+ language="pol",
233
+ delay=0,
234
+ ),
235
+ AudioStreamInfo(
236
+ id="dc57b3f8fdb04420bc820fe1d45985b6",
237
+ type="audio",
238
+ index=6,
239
+ codec_name="mp2",
240
+ language="aux",
241
+ format="s16p",
242
+ channels=2,
243
+ channels_layout="stereo",
244
+ sample_rate=48000,
245
+ bit_rate=128000,
246
+ frame_size=1152,
247
+ delay=0,
248
+ ),
249
+ ],
250
+ pipeline_stages=[
251
+ PipelineStageReference(
252
+ id="c577dd6d2be743d5a155ef75d6d1ec79",
253
+ created_at="2024-08-30T11:09:55Z",
254
+ created_by="stream-chunker",
255
+ output_topic_name="reference-chunks",
256
+ parent_ids=[],
257
+ data={},
258
+ ),
259
+ PipelineStageReference(
260
+ id="78c7392659984c2d89057bd628275855",
261
+ created_at="2024-08-30T11:09:56Z",
262
+ created_by="chunk-transformer",
263
+ parent_ids=[],
264
+ output_topic_name="reference-transformed-chunks",
265
+ data={
266
+ "id": "f11a8990bfff4288b9b284cb9908c788",
267
+ "created_at": "2024-08-30T11:09:55Z",
268
+ "created_by": "stream-chunker",
269
+ "s3_key": "0285ee22c95f43f9a98792d210aa6813.mpegts",
270
+ "s3_bucket": "reference-chunks",
271
+ "chunk_duration": 5,
272
+ "chunk_overlap_sec": 0,
273
+ "chunk_size": 5179024,
274
+ "ignored_streams": [3, 5, 7],
275
+ },
276
+ ),
277
+ ],
278
+ ),
279
+ )
280
+
281
+
282
+ def test_parse_1() -> None:
283
+ parser = InputParser(InputMessagePayload)
284
+ obj = parser.parse_json(input_json_value)
285
+
286
+ assert obj.data == expected.data
287
+ assert obj.data.chunks == expected.data.chunks
288
+ assert obj.id == expected.id
289
+ assert obj.created_at == expected.created_at
290
+ assert obj.created_by == expected.created_by
291
+ assert obj.metadata.origin == expected.metadata.origin
292
+ assert obj.metadata.pipeline_stages == expected.metadata.pipeline_stages
293
+ assert obj.metadata.streams == expected.metadata.streams
294
+ assert obj == expected
295
+ assert obj.model_dump_json() == input_json_value
296
+
297
+
298
+ def test_parse_mesage_raise() -> None:
299
+ parser = InputParser(InputMessagePayload)
300
+
301
+ with pytest.raises(IncorrectMessageValueError):
302
+ parser.parse_message(
303
+ KafkaMessage(
304
+ value=None,
305
+ key=fake.text(),
306
+ topic=fake.text(),
307
+ offset=fake.random_int(),
308
+ partition=fake.random_int(),
309
+ )
310
+ )