atk-common 2.1.0__py3-none-any.whl → 3.0.0__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 (34) hide show
  1. atk_common/__init__.py +0 -66
  2. atk_common/bo_logger.py +9 -6
  3. atk_common/classes/__init__.py +16 -0
  4. atk_common/classes/bo_logger.py +62 -0
  5. atk_common/classes/docker_handler.py +76 -0
  6. atk_common/classes/env_handler.py +38 -0
  7. atk_common/classes/error_handler.py +81 -0
  8. atk_common/classes/http_response_handler.py +42 -0
  9. atk_common/classes/rabbitmq_consumer.py +72 -0
  10. atk_common/http_response_utils.py +1 -1
  11. atk_common/interfaces/__init__.py +14 -0
  12. atk_common/interfaces/docker_handler_interface.py +9 -0
  13. atk_common/interfaces/env_handler_interface.py +8 -0
  14. atk_common/interfaces/error_handler_interface.py +11 -0
  15. atk_common/interfaces/http_response_handler_interface.py +5 -0
  16. atk_common/interfaces/logger_interface.py +9 -0
  17. atk_common/mq_utils.py +1 -1
  18. atk_common/utils/__init__.py +47 -0
  19. atk_common/utils/consumer_retry_handler.py +55 -0
  20. atk_common/utils/datetime_utils.py +106 -0
  21. atk_common/utils/db_utils.py +25 -0
  22. atk_common/utils/default_should_retry.py +19 -0
  23. atk_common/utils/error_utils.py +5 -0
  24. atk_common/utils/file_utils.py +4 -0
  25. atk_common/utils/hash_utils.py +21 -0
  26. atk_common/utils/http_utils.py +18 -0
  27. atk_common/utils/internal_response_utils.py +20 -0
  28. atk_common/utils/mq_utils.py +30 -0
  29. {atk_common-2.1.0.dist-info → atk_common-3.0.0.dist-info}/METADATA +1 -1
  30. {atk_common-2.1.0.dist-info → atk_common-3.0.0.dist-info}/RECORD +34 -10
  31. atk_package/__init__.py +2 -2
  32. {atk_common-2.1.0.dist-info → atk_common-3.0.0.dist-info}/WHEEL +0 -0
  33. {atk_common-2.1.0.dist-info → atk_common-3.0.0.dist-info}/licenses/license.txt +0 -0
  34. {atk_common-2.1.0.dist-info → atk_common-3.0.0.dist-info}/top_level.txt +0 -0
atk_common/__init__.py CHANGED
@@ -1,67 +1 @@
1
1
  # __init__.py
2
- from atk_common.bo_logger import BoLogger
3
- from atk_common.consumer_retry_handler import create_retry_handler
4
- from atk_common.datetime_utils import \
5
- get_utc_date_time, \
6
- get_utc_date_time_str, \
7
- get_utc_date_time_str_with_z, \
8
- seconds_to_utc_timestamp, \
9
- get_utc_date_from_iso, \
10
- adjust_millisescond, \
11
- convert_to_utc, \
12
- convert_to_utc_image_dt
13
- from atk_common.db_utils import sql, sql_with_record, convert_none_to_null, date_time_utc_column
14
- from atk_common.default_should_retry import default_should_retry
15
- from atk_common.docker_utils import get_current_container_info, set_container_metadata
16
- from atk_common.env_utils import get_env_value
17
- from atk_common.error_utils import get_message, create_error_log, get_error_entity, resend_error_entity, handle_error, get_response_error, get_error_type
18
- from atk_common.http_response_utils import http_response
19
- from atk_common.hash_utils import create_enforcement_hash
20
- from atk_common.http_utils import is_http_status_ok, is_http_status_internal, get_test_response
21
- from atk_common.internal_response_utils import create_response, is_response_ok, is_response_http, is_response_internal
22
- from atk_common.file_utils import get_image_file_type
23
- from atk_common.log_utils import add_log_item, add_log_item_http
24
- from atk_common.mq_utils import decode_message
25
- from atk_common.rabbitmq_consumer import RabbitMQConsumer
26
-
27
- __all__ = [
28
- 'BoLogger',
29
- 'create_retry_handler',
30
- 'get_utc_date_time',
31
- 'get_utc_date_time_str',
32
- 'get_utc_date_time_str_with_z',
33
- 'seconds_to_utc_timestamp',
34
- 'get_utc_date_from_iso',
35
- 'adjust_millisescond',
36
- 'convert_to_utc',
37
- 'convert_to_utc_image_dt',
38
- 'sql',
39
- 'sql_with_record',
40
- 'convert_none_to_null',
41
- 'date_time_utc_column',
42
- 'default_should_retry',
43
- 'get_current_container_info',
44
- 'set_container_metadata',
45
- 'get_env_value',
46
- 'get_message',
47
- 'create_error_log',
48
- 'get_error_entity',
49
- 'resend_error_entity',
50
- 'handle_error',
51
- 'get_response_error',
52
- 'get_error_type',
53
- 'create_enforcement_hash',
54
- 'http_response',
55
- 'is_http_status_ok',
56
- 'is_http_status_internal',
57
- 'get_test_response',
58
- 'create_response',
59
- 'is_response_ok',
60
- 'is_response_http',
61
- 'is_response_internal',
62
- 'get_image_file_type',
63
- 'add_log_item',
64
- 'add_log_item_http',
65
- 'decode_message',
66
- 'RabbitMQConsumer',
67
- ]
atk_common/bo_logger.py CHANGED
@@ -5,7 +5,10 @@ from atk_common.enums.log_level_enum import LogLevel
5
5
 
6
6
  class BoLogger:
7
7
  def __init__(self, log_level: LogLevel, log_url):
8
- self.log_level = log_level
8
+ if log_level is None:
9
+ self.log_level = LogLevel.INFO.value
10
+ else:
11
+ self.log_level = log_level
9
12
  self.log_url = log_url
10
13
 
11
14
  def set_level(self, log_level):
@@ -28,16 +31,16 @@ class BoLogger:
28
31
  # Send log_json to self.log_url via HTTP POST request (Grafana Loki or similar)
29
32
 
30
33
  def debug(self, message: str):
31
- self._log(LogLevel.DEBUG, message)
34
+ self._log(LogLevel.DEBUG.value, message)
32
35
 
33
36
  def info(self, message: str):
34
- self._log(LogLevel.INFO, message)
37
+ self._log(LogLevel.INFO.value, message)
35
38
 
36
39
  def warning(self, message: str):
37
- self._log(LogLevel.WARNING, message)
40
+ self._log(LogLevel.WARNING.value, message)
38
41
 
39
42
  def error(self, message: str):
40
- self._log(LogLevel.ERROR, message)
43
+ self._log(LogLevel.ERROR.value, message)
41
44
 
42
45
  def critical(self, message: str):
43
- self._log(LogLevel.CRITICAL, message)
46
+ self._log(LogLevel.CRITICAL.value, message)
@@ -0,0 +1,16 @@
1
+ # __init__.py
2
+ from atk_common.classes.bo_logger import BoLogger
3
+ from atk_common.classes.docker_handler import DockerHandler
4
+ from atk_common.classes.env_handler import EnvHandler
5
+ from atk_common.classes.error_handler import ErrorHandler
6
+ from atk_common.classes.http_response_handler import HttpResponseHandler
7
+ from atk_common.classes.rabbitmq_consumer import RabbitMQConsumer
8
+
9
+ __all__ = [
10
+ 'BoLogger',
11
+ 'DockerHandler',
12
+ 'EnvHandler',
13
+ 'ErrorHandler',
14
+ 'HttpResponseHandler',
15
+ 'RabbitMQConsumer',
16
+ ]
@@ -0,0 +1,62 @@
1
+ import json
2
+ from typing import Optional
3
+ from opentelemetry.trace import get_current_span
4
+ from atk_common.interfaces import ILogger # for type hints only
5
+ from atk_common.enums import LogLevel
6
+ from atk_common.utils.datetime_utils import get_utc_date_time_str
7
+ from atk_common.enums.log_level_enum import LogLevel
8
+
9
+ class BoLogger(ILogger):
10
+ def __init__(self, log_level: LogLevel, component, version):
11
+ if log_level is None:
12
+ self.log_level = LogLevel.INFO
13
+ else:
14
+ self.log_level = log_level
15
+ self.component = component
16
+ self.version = version
17
+
18
+ def _get_trace_context(self):
19
+ span = get_current_span()
20
+ ctx = span.get_span_context()
21
+ if ctx.is_valid:
22
+ return {
23
+ "trace_id": f"{ctx.trace_id:032x}",
24
+ "span_id": f"{ctx.span_id:016x}",
25
+ }
26
+ return {"trace_id": None, "span_id": None}
27
+
28
+ def _create_log_json(self, timestamp, level: LogLevel, message: str):
29
+ log_entry = {
30
+ "timestamp": timestamp,
31
+ "severity": level.name,
32
+ "message": message,
33
+ "component": self.component,
34
+ "version": self.version,
35
+ }
36
+ log_entry.update(self._get_trace_context())
37
+ return log_entry
38
+
39
+ def _log(self, level: LogLevel, message: str):
40
+ if level.value >= self.log_level.value:
41
+ timestamp = get_utc_date_time_str()
42
+ log_json = self._create_log_json(timestamp, level, message)
43
+ print(json.dumps(log_json))
44
+
45
+ def debug(self, message: str):
46
+ self._log(LogLevel.DEBUG, message)
47
+
48
+ def info(self, message: str):
49
+ self._log(LogLevel.INFO, message)
50
+
51
+ def warning(self, message: str):
52
+ self._log(LogLevel.WARNING, message)
53
+
54
+ def error(self, message: str):
55
+ self._log(LogLevel.ERROR, message)
56
+
57
+ def critical(self, message: str):
58
+ self._log(LogLevel.CRITICAL, message)
59
+
60
+ def set_level(self, log_level: LogLevel):
61
+ self.log_level = log_level
62
+
@@ -0,0 +1,76 @@
1
+ import docker
2
+ import socket
3
+ from atk_common.interfaces import IDockerHandler
4
+ from atk_common.interfaces import ILogger
5
+
6
+ class DockerHandler(IDockerHandler):
7
+ def __init__(self, logger: ILogger, image_name, image_version):
8
+ self.logger = logger
9
+ self.image_name = image_name
10
+ self.image_version = image_version
11
+ self.set_container_metadata()
12
+
13
+ def get_image_name_and_version(self, tags):
14
+ if tags:
15
+ # Use the first tag (usually only one)
16
+ full_tag = tags[0] # e.g., 'bo-crypto-wrapper-api:latest'
17
+
18
+ if ":" in full_tag:
19
+ image_name, image_version = full_tag.split(":", 1)
20
+ else:
21
+ image_name = full_tag
22
+ image_version = "<none>"
23
+ return image_name, image_version
24
+ else:
25
+ return None, None
26
+
27
+ def create_port_item(self, port, binding):
28
+ data = {}
29
+ data['port'] = port
30
+ data['binding'] = binding
31
+ return data
32
+
33
+ def create_container_log(self, container_data):
34
+ image_name = container_data.get('imageName') or 'Unknown'
35
+ image_version = container_data.get('imageVersion') or 'Unknown'
36
+
37
+ log_str = f'Image name: {image_name}, image version: {image_version}'
38
+ self.logger.info(log_str)
39
+
40
+ def get_current_container_info(self):
41
+ try:
42
+ data = {}
43
+ client = docker.from_env()
44
+
45
+ # Get current container's hostname (usually the container ID)
46
+ container_id = socket.gethostname()
47
+
48
+ # Fetch container object using partial ID
49
+ container = client.containers.get(container_id)
50
+
51
+ tags_info = self.get_image_name_and_version(container.image.tags)
52
+ data['imageName'] = tags_info[0]
53
+ data['imageVersion'] = tags_info[1]
54
+ data['containerName'] = container.name
55
+ ports = container.attrs['NetworkSettings']['Ports']
56
+ data['ports'] = []
57
+ if ports:
58
+ for container_port, host_bindings in ports.items():
59
+ if host_bindings:
60
+ for binding in host_bindings:
61
+ data['ports'].append(self.create_port_item(container_port, f"{binding['HostIp']}:{binding['HostPort']}"))
62
+ else:
63
+ data['ports'].append(self.create_port_item(container_port, None))
64
+ self.create_container_log(data)
65
+ return data
66
+ except Exception as e:
67
+ self.logger.error("Error getting container data:" + str(e))
68
+ return None
69
+
70
+ def set_container_metadata(self):
71
+ data = {}
72
+ data['imageName'] = self.image_name
73
+ data['imageVersion'] = self.image_version
74
+ data['containerName'] = self.image_name
75
+ self.create_container_log(data)
76
+ return data
@@ -0,0 +1,38 @@
1
+ import os
2
+ from typing import Optional
3
+ from atk_common.interfaces import IEnvHandler
4
+ from atk_common.interfaces import ILogger
5
+
6
+ class EnvHandler(IEnvHandler):
7
+ def __init__(self, logger: Optional(ILogger)): # type: ignore
8
+ self.logger = logger
9
+
10
+ def val_str(self, value):
11
+ if value is None:
12
+ return '<Empty>'
13
+ if isinstance(value, str):
14
+ if value.strip() == '' or value.lower() == 'null':
15
+ return '<Null>'
16
+ return value
17
+ return str(value)
18
+
19
+ def is_value_null_or_empty(self, value):
20
+ if isinstance(value, str):
21
+ return value.strip() == '' or value.lower() == 'null'
22
+ return False
23
+
24
+ def get_env_value(self, key, abort_on_error=True):
25
+ val = os.environ.get(key)
26
+ if self.logger:
27
+ self.logger.info(key + ':' + self.val_str(val))
28
+ if val is None and abort_on_error:
29
+ err_msg = f"Environment variable '{key}' is not set."
30
+ if self.logger:
31
+ self.logger.critical(err_msg)
32
+ raise ValueError(err_msg)
33
+ if self.is_value_null_or_empty(val):
34
+ return None
35
+ return val
36
+
37
+ def set_logger(self, logger: ILogger):
38
+ self.logger = logger
@@ -0,0 +1,81 @@
1
+ from http import HTTPStatus
2
+ from flask import Response
3
+ from enum import Enum
4
+ import json
5
+ from typing import Optional
6
+ from atk_common.utils.datetime_utils import get_utc_date_time_str
7
+ from atk_common.enums.api_error_type_enum import ApiErrorType
8
+ from atk_common.utils.http_utils import is_http_status_internal
9
+ from atk_common.utils.internal_response_utils import create_response
10
+ from atk_common.interfaces import IErrorHandler
11
+ from atk_common.interfaces import ILogger
12
+ from atk_common.interfaces import IDockerHandler
13
+
14
+ class ErrorHandler(IErrorHandler):
15
+ def __init__(self, logger: ILogger, docker_handler: IDockerHandler):
16
+ self.logger = logger
17
+ self.docker_handler = docker_handler
18
+
19
+ def get_message(self, error):
20
+ if hasattr(error, 'message'):
21
+ return str(error.message)
22
+ else:
23
+ return str(error)
24
+
25
+ def create_error_log(self, data):
26
+ err_str = data['message'] + ', statusCode: ' + str(data['statusCode']) + ', method: ' + data['method']
27
+ if data['containerInfo'] is not None:
28
+ err_str += ', imageName: ' + data['containerInfo']['imageName']
29
+ err_str += ', imageVersion: ' + data['containerInfo']['imageVersion']
30
+ err_str += ', containerName: ' + data['containerInfo']['containerName']
31
+ else:
32
+ err_str += ', imageName: <none>'
33
+ err_str += ', imageVersion: <none>'
34
+ err_str += ', containerName: <none>'
35
+ self.logger.critical(err_str)
36
+
37
+ def get_error_entity(self, error, method, error_type, status_code, container_info):
38
+ data = {}
39
+ data['statusCode'] = status_code
40
+ data['exceptionType'] = str(type(error))
41
+ data['errorType'] = error_type.value if isinstance(error_type, Enum) else error_type
42
+ data['message'] = self.get_message(error)
43
+ data['method'] = method
44
+ data['timestamp'] = get_utc_date_time_str()
45
+ data['containerInfo'] = container_info
46
+ self.create_error_log(data)
47
+ return Response(
48
+ response=json.dumps(data),
49
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
50
+ mimetype='application/json'
51
+ )
52
+
53
+ def resend_error_entity(self, error_entity):
54
+ self.create_error_log(error_entity)
55
+ return Response(
56
+ response=json.dumps(error_entity),
57
+ status=HTTPStatus.INTERNAL_SERVER_ERROR,
58
+ mimetype='application/json'
59
+ )
60
+
61
+ def handle_error(self, resp, status):
62
+ if is_http_status_internal(resp.status_code):
63
+ self.logger.info(resp.json().get('message'))
64
+ return create_response(status, resp.status_code, resp.json())
65
+ else:
66
+ self.logger.info(resp.text)
67
+ return create_response(status, resp.status_code, resp.text)
68
+
69
+ def get_response_error(self, resp):
70
+ if is_http_status_internal(resp.status_code):
71
+ return resp.json()
72
+ else:
73
+ return resp.text
74
+
75
+ # Return values:
76
+ # 1 - Connection error
77
+ # 2 - Internal error
78
+ def get_error_type(self, conn):
79
+ if conn is None:
80
+ return ApiErrorType.CONNECTION
81
+ return ApiErrorType.INTERNAL
@@ -0,0 +1,42 @@
1
+ import json
2
+ from flask import Response
3
+ from http import HTTPStatus
4
+ from atk_common.enums.api_error_type_enum import ApiErrorType
5
+ from atk_common.utils.http_utils import is_http_status_internal, is_http_status_ok
6
+ from atk_common.utils.internal_response_utils import is_response_http
7
+ from atk_common.interfaces import IErrorHandler
8
+ from atk_common.interfaces import IHttpResponseHandler
9
+
10
+ class HttpResponseHandler(IHttpResponseHandler):
11
+ def __init__(self, error_handler: IErrorHandler):
12
+ self.error_handler = error_handler
13
+
14
+ def _convert_response_data(self, data):
15
+ if isinstance(data, dict):
16
+ return json.dumps(data)
17
+ elif isinstance(data, list):
18
+ return json.dumps(data)
19
+ elif isinstance(data, str):
20
+ json_data = json.loads(data)
21
+ return json.dumps(json_data)
22
+ else:
23
+ return data
24
+
25
+ # If response['status'] == 0 (OK, http status = 200): create Response and return response['responseMsg']
26
+ # If http status == 500:
27
+ # If response['status'] == 1 (HTTP): resend received error entity
28
+ # If response['status'] == 2 (INTERNAL): create new error entity and return as response
29
+ # If http status other value: create new error entity and return as response
30
+ def http_response(self, method, response, container_info):
31
+ if is_http_status_ok(response['statusCode']):
32
+ return Response(
33
+ response=self._convert_response_data(response['responseMsg']),
34
+ status=HTTPStatus.OK,
35
+ mimetype=response['contentType'],
36
+ headers=response['httpHeaders']
37
+ )
38
+ elif is_http_status_internal(response['statusCode']):
39
+ if is_response_http(response):
40
+ return self.error_handler.resend_error_entity(response['responseMsg'])
41
+ return self.error_handler.get_error_entity(response['responseMsg'], method, ApiErrorType.INTERNAL, response['statusCode'], container_info)
42
+ return self.error_handler.get_error_entity(response['responseMsg'], method, ApiErrorType.CONNECTION, response['statusCode'], container_info)
@@ -0,0 +1,72 @@
1
+ from kombu import Connection, Queue, Consumer
2
+ from kombu.exceptions import ConnectionError, ChannelError # Import relevant exceptions
3
+ import socket
4
+ import time
5
+ from atk_common.interfaces import ILogger
6
+ CONNECTION_ERRORS = (
7
+ ConnectionError,
8
+ ChannelError,
9
+ OSError,
10
+ )
11
+
12
+ class RabbitMQConsumer:
13
+ def __init__(self, queue_name, user, pwd, host, vhost, dlx, dlq, content_type, message_handler, logger: ILogger):
14
+ self.logger = logger
15
+
16
+ rabbit_url = 'amqp://' + user + ':' + pwd + '@' + host + '/' + vhost
17
+ self.logger.info(f"Initializing connection to RabbitMQ {queue_name} at {host}")
18
+
19
+ self.connection = Connection(rabbit_url, heartbeat=10)
20
+ if dlx is not None and dlq is not None:
21
+ queue = Queue(name=queue_name,
22
+ queue_arguments={
23
+ 'x-dead-letter-exchange': dlx,
24
+ 'x-dead-letter-routing-key': dlq},
25
+ )
26
+ else:
27
+ queue = Queue(name=queue_name)
28
+ self.queue = queue
29
+ self.content_type = content_type # Optional content type for message handling
30
+ self.message_handler = message_handler # Custom message handler
31
+
32
+ def consume(self):
33
+ conn = self.establish_connection()
34
+ self.logger.info("Begin consuming messages...")
35
+
36
+ while True:
37
+ try:
38
+ conn.drain_events(timeout=2)
39
+ self.logger.debug("Drained event or heartbeat.")
40
+ except socket.timeout:
41
+ self.logger.debug("Socket timeout, checking heartbeat...")
42
+ conn.heartbeat_check()
43
+ except CONNECTION_ERRORS as e:
44
+ self.logger.critical(f"Connection lost: {e}. Reconnecting...")
45
+ return # break loop and re-establish connection
46
+ except Exception as e:
47
+ self.logger.critical(f"Top-level exception in consume loop: {e}. Restarting after delay...")
48
+ return
49
+
50
+ def establish_connection(self):
51
+ revived_connection = self.connection.clone()
52
+ revived_connection.ensure_connection(max_retries=3)
53
+ channel = revived_connection.channel()
54
+
55
+ consumer = Consumer(
56
+ revived_connection,
57
+ queues=self.queue,
58
+ callbacks=[self.message_handler],
59
+ accept=[self.content_type] if self.content_type else None)
60
+ consumer.revive(channel)
61
+ consumer.consume()
62
+ self.logger.info("Connection revived!")
63
+ return revived_connection
64
+
65
+ def run(self):
66
+ self.logger.info("Starting RabbitMQ consumer run loop...")
67
+ while True:
68
+ try:
69
+ self.consume()
70
+ except Exception as e:
71
+ self.logger.critical(f"Top-level exception in run loop: {e}. Restarting after delay...")
72
+ time.sleep(5)
@@ -2,7 +2,7 @@ import json
2
2
  from flask import Response
3
3
  from http import HTTPStatus
4
4
  from atk_common.enums.api_error_type_enum import ApiErrorType
5
- from atk_common.error_utils import get_error_entity, resend_error_entity
5
+ from atk_common.utils.error_utils import get_error_entity, resend_error_entity
6
6
  from atk_common.http_utils import is_http_status_internal, is_http_status_ok
7
7
  from atk_common.internal_response_utils import is_response_http
8
8
 
@@ -0,0 +1,14 @@
1
+ # __init__.py
2
+ from atk_common.interfaces.docker_handler_interface import IDockerHandler
3
+ from atk_common.interfaces.env_handler_interface import IEnvHandler
4
+ from atk_common.interfaces.error_handler_interface import IErrorHandler
5
+ from atk_common.interfaces.http_response_handler_interface import IHttpResponseHandler
6
+ from atk_common.interfaces.logger_interface import ILogger
7
+
8
+ __all__ = [
9
+ 'IDockerHandler',
10
+ 'IEnvHandler',
11
+ 'IErrorHandler',
12
+ 'ILogger',
13
+ 'IHttpResponseHandler',
14
+ ]
@@ -0,0 +1,9 @@
1
+ # interfaces/logger_interface.py
2
+ from typing import Protocol
3
+
4
+ class IDockerHandler(Protocol):
5
+ def get_image_name_and_version(self, tags) -> None: ...
6
+ def create_port_item(port, binding) -> None: ...
7
+ def create_container_log(container_data) -> None: ...
8
+ def get_current_container_info() -> None: ...
9
+ def set_container_metadata(image_name_env_key, image_version_env_key) -> None: ...
@@ -0,0 +1,8 @@
1
+ # interfaces/env_handler_interface.py
2
+ from typing import Protocol
3
+
4
+ class IEnvHandler(Protocol):
5
+ def val_str(self, value) -> None: ...
6
+ def is_value_null_or_empty(self, value) -> None: ...
7
+ def get_env_value(self, msg: str) -> None: ...
8
+ def set_logger(self, logger) -> None: ...
@@ -0,0 +1,11 @@
1
+ # interfaces/error_handler_interface.py
2
+ from typing import Protocol
3
+
4
+ class IErrorHandler(Protocol):
5
+ def get_message(self, error) -> None: ...
6
+ def create_error_log(self, data) -> None: ...
7
+ def get_error_entity(self, error, method, error_type, status_code, container_info) -> None: ...
8
+ def resend_error_entity(self, error_entity) -> None: ...
9
+ def handle_error(self, resp, status) -> None: ...
10
+ def get_response_error(self, resp) -> None: ...
11
+ def get_error_type(self, conn) -> None: ...
@@ -0,0 +1,5 @@
1
+ # interfaces/env_handler_interface.py
2
+ from typing import Protocol
3
+
4
+ class IHttpResponseHandler(Protocol):
5
+ def http_response(self, method, response, container_info) -> None: ...
@@ -0,0 +1,9 @@
1
+ # interfaces/logger_interface.py
2
+ from typing import Protocol
3
+
4
+ class ILogger(Protocol):
5
+ def debug(self, msg: str) -> None: ...
6
+ def info(self, msg: str) -> None: ...
7
+ def warning(self, msg: str) -> None: ...
8
+ def error(self, msg: str) -> None: ...
9
+ def critical(self, msg: str) -> None: ...
atk_common/mq_utils.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import json
2
- from atk_common.error_utils import get_message
2
+ from atk_common.utils.error_utils import get_message
3
3
  from atk_common.log_utils import add_log_item
4
4
 
5
5
  def decode_message(body, message):
@@ -0,0 +1,47 @@
1
+ # __init__.py
2
+ from atk_common.utils.consumer_retry_handler import create_retry_handler
3
+ from atk_common.utils.datetime_utils import \
4
+ get_utc_date_time, \
5
+ get_utc_date_time_str, \
6
+ get_utc_date_time_str_with_z, \
7
+ seconds_to_utc_timestamp, \
8
+ get_utc_date_from_iso, \
9
+ adjust_millisescond, \
10
+ convert_to_utc, \
11
+ convert_to_utc_image_dt
12
+ from atk_common.utils.db_utils import sql, sql_with_record, convert_none_to_null, date_time_utc_column
13
+ from atk_common.utils.default_should_retry import default_should_retry
14
+ from atk_common.utils.error_utils import get_message
15
+ from atk_common.utils.hash_utils import create_enforcement_hash
16
+ from atk_common.utils.http_utils import is_http_status_ok, is_http_status_internal, get_test_response
17
+ from atk_common.utils.internal_response_utils import create_response, is_response_ok, is_response_http, is_response_internal
18
+ from atk_common.utils.file_utils import get_image_file_type
19
+ from atk_common.utils.mq_utils import decode_message
20
+
21
+ __all__ = [
22
+ 'create_retry_handler',
23
+ 'get_utc_date_time',
24
+ 'get_utc_date_time_str',
25
+ 'get_utc_date_time_str_with_z',
26
+ 'seconds_to_utc_timestamp',
27
+ 'get_utc_date_from_iso',
28
+ 'adjust_millisescond',
29
+ 'convert_to_utc',
30
+ 'convert_to_utc_image_dt',
31
+ 'sql',
32
+ 'sql_with_record',
33
+ 'convert_none_to_null',
34
+ 'date_time_utc_column',
35
+ 'default_should_retry',
36
+ 'get_message',
37
+ 'create_enforcement_hash',
38
+ 'is_http_status_ok',
39
+ 'is_http_status_internal',
40
+ 'get_test_response',
41
+ 'create_response',
42
+ 'is_response_ok',
43
+ 'is_response_http',
44
+ 'is_response_internal',
45
+ 'get_image_file_type',
46
+ 'decode_message',
47
+ ]
@@ -0,0 +1,55 @@
1
+ from kombu import Producer
2
+ from atk_common.classes.bo_logger import BoLogger
3
+ from atk_common.utils.internal_response_utils import is_response_ok
4
+
5
+ """
6
+ process_func: your original handler function (body, message)
7
+ connection: kombu.Connection instance to use for retrying (nullable)
8
+ exchange: kombu.Exchange instance to publish to (nullable)
9
+ routing_key: routing key for the exchange (nullable)
10
+ should_retry: function(message_status) -> bool
11
+ bo_logger: BoLogger instance for logging
12
+ declare: optional kombu.DeclarativeExchange or Queue to declare before publishing
13
+ """
14
+ def create_retry_handler(process_func, connection, exchange, routing_key, should_retry, bo_logger: BoLogger, declare=None):
15
+
16
+ def handler(body, message):
17
+ try:
18
+ process_response = process_func(body, message)
19
+ if is_response_ok(process_response):
20
+ message.ack()
21
+ else:
22
+ if connection is not None:
23
+ # Use retry queue
24
+ if should_retry(process_response):
25
+ bo_logger.info("Retrying after delay...")
26
+ with connection.Producer() as producer:
27
+ producer.publish(
28
+ message.body,
29
+ exchange=exchange,
30
+ routing_key=routing_key,
31
+ retry=True,
32
+ declare=declare,
33
+ content_type=message.content_type,
34
+ content_encoding=message.content_encoding,
35
+ headers=message.headers,
36
+ timestamp=message.properties.get("timestamp")
37
+ )
38
+ message.ack()
39
+ else:
40
+ bo_logger.critical("Sending to DLQ...")
41
+ message.reject(requeue=False)
42
+ else:
43
+ if should_retry(process_response):
44
+ bo_logger.critical("Sending to DLQ...")
45
+ message.reject(requeue=False)
46
+ # bo_logger.critical("Requing...")
47
+ # message.requeue()
48
+ else:
49
+ bo_logger.critical("Discarding message...")
50
+ message.ack()
51
+ except Exception as e:
52
+ bo_logger.critical(f"Error during processing: {e}, sending to DLQ...")
53
+ message.reject(requeue=False)
54
+
55
+ return handler
@@ -0,0 +1,106 @@
1
+ from datetime import datetime, timezone, timedelta
2
+ from zoneinfo import ZoneInfo
3
+
4
+ def get_utc_date_time():
5
+ return datetime.now(timezone.utc)
6
+
7
+ def get_utc_date_time_str():
8
+ return str(datetime.now(timezone.utc))
9
+
10
+ def get_utc_date_time_str_with_z():
11
+ return datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
12
+
13
+ def seconds_to_utc_timestamp(seconds):
14
+ return str(datetime.fromtimestamp(seconds, tz=timezone.utc))
15
+
16
+ def get_utc_date_from_iso(date_time):
17
+ dt = datetime.fromisoformat(date_time)
18
+ dt_utc = dt.astimezone(timezone.utc)
19
+ return str(dt_utc.date())
20
+
21
+ def adjust_millisescond(dt_str):
22
+ if '.' in dt_str:
23
+ dt_part, frac = dt_str.split('.')
24
+ frac = frac[:6] # keep only the first 6 digits
25
+ dt_str_clean = f"{dt_part}.{frac}"
26
+ return dt_str_clean
27
+ else:
28
+ dt_str_clean = dt_str
29
+ return dt_str_clean
30
+
31
+ # Original datetime string, handles:
32
+ # dt_str = '2023-02-01T14:13:08.653133+01:00'
33
+ # dt_str = '2023-02-01T14:13:08.653133 01:00'
34
+ # dt_str = '2024-01-03T08:13:52.705474147'
35
+ def convert_to_utc(dt_str):
36
+ # Split datetime and offset
37
+ if '+' in dt_str:
38
+ parts = dt_str.split('+')
39
+ else:
40
+ parts = dt_str.split(' ')
41
+ if len(parts) == 2:
42
+ dt_part, offset_part = parts
43
+ else:
44
+ dt_part = parts[0]
45
+ offset_part = None # or set a default if needed
46
+
47
+ dt_part = adjust_millisescond(dt_part)
48
+ dt_naive = datetime.fromisoformat(dt_part)
49
+
50
+ if offset_part is None:
51
+ dt_aware = dt_naive.replace(tzinfo=ZoneInfo("Europe/Berlin"))
52
+ dt_utc = dt_aware.astimezone(ZoneInfo("UTC"))
53
+ return dt_utc.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
54
+ else:
55
+ # Create a timezone object using the offset
56
+ hours_offset = int(offset_part.split(':')[0])
57
+ minutes_offset = int(offset_part.split(':')[1])
58
+ tzinfo = timezone(timedelta(hours=hours_offset, minutes=minutes_offset))
59
+
60
+ # Assign the timezone to the datetime
61
+ dt_with_tz = dt_naive.replace(tzinfo=tzinfo)
62
+
63
+ # Convert to UTC
64
+ dt_utc =dt_with_tz.astimezone(timezone.utc)
65
+ return dt_utc.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
66
+
67
+ # Original datetime string, handles:
68
+ # dt_str = '2023-02-01 14:13:08.653133 +01:00'
69
+ def convert_to_utc_image_dt(dt_str):
70
+ dt_str = dt_str.strip()
71
+ # Detect offset
72
+ if '+' in dt_str:
73
+ parts = dt_str.split('+')
74
+ sign = '+'
75
+ elif '-' in dt_str[10:]: # avoid confusion with date part
76
+ parts = dt_str.split('-')
77
+ sign = '-'
78
+ else:
79
+ parts = [dt_str]
80
+ sign = None
81
+
82
+ if len(parts) == 2:
83
+ dt_part = parts[0].strip()
84
+ offset_part = parts[1].strip()
85
+ if sign == '-':
86
+ offset_part = '-' + offset_part
87
+ else:
88
+ offset_part = '+' + offset_part
89
+ else:
90
+ dt_part = parts[0].strip()
91
+ offset_part = None
92
+
93
+ dt_part = adjust_millisescond(dt_part)
94
+ dt_naive = datetime.fromisoformat(dt_part)
95
+
96
+ if offset_part is None:
97
+ dt_aware = dt_naive.replace(tzinfo=ZoneInfo("Europe/Berlin"))
98
+ dt_utc = dt_aware.astimezone(ZoneInfo("UTC"))
99
+ else:
100
+ offset_hours, offset_minutes = map(int, offset_part.split(':'))
101
+ delta = timedelta(hours=offset_hours, minutes=offset_minutes)
102
+ tz = timezone(delta)
103
+ dt_aware = dt_naive.replace(tzinfo=tz)
104
+ dt_utc = dt_aware.astimezone(timezone.utc)
105
+
106
+ return dt_utc.strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
@@ -0,0 +1,25 @@
1
+ def sql(connection, sql, get_one):
2
+ from psycopg2.extras import RealDictCursor
3
+ with connection:
4
+ with connection.cursor(cursor_factory=RealDictCursor) as cursor:
5
+ cursor.execute(sql)
6
+ if get_one:
7
+ return cursor.fetchone()
8
+ return cursor.fetchall()
9
+
10
+ def sql_with_record(connection, sql, record):
11
+ from psycopg2.extras import RealDictCursor
12
+ with connection:
13
+ with connection.cursor(cursor_factory=RealDictCursor) as cursor:
14
+ cursor.execute(sql, record)
15
+ return cursor.fetchone()
16
+
17
+ def convert_none_to_null(value):
18
+ if value is None or value == '':
19
+ return 'null'
20
+ if type(value) is str:
21
+ return '\'' + value + '\''
22
+ return value
23
+
24
+ def date_time_utc_column(column):
25
+ return 'TO_CHAR({0} at time zone \'UTC\', \'yyyy-mm-dd hh24:mi:ss.ms"Z"\')'.format(column)
@@ -0,0 +1,19 @@
1
+ from atk_common.enums.api_error_type_enum import ApiErrorType
2
+ from atk_common.enums.response_status_type_enum import ResponseStatusType
3
+ from atk_common.utils.http_utils import is_http_status_internal, is_http_status_ok
4
+
5
+ def default_should_retry(message_status):
6
+ if message_status['status'] == ResponseStatusType.HTTP:
7
+ status_code = message_status['statusCode']
8
+ if is_http_status_ok(status_code):
9
+ return False
10
+ elif is_http_status_internal(status_code):
11
+ #errorType = 1: connection error
12
+ #errorType = 2: api internal error (database, etc)
13
+ if message_status['responseMsg']['errorType'] == ApiErrorType.CONNECTION.value:
14
+ return True
15
+ else:
16
+ return False
17
+ else:
18
+ return True
19
+ return True
@@ -0,0 +1,5 @@
1
+ def get_message(error):
2
+ if hasattr(error, 'message'):
3
+ return str(error.message)
4
+ else:
5
+ return str(error)
@@ -0,0 +1,4 @@
1
+ def get_image_file_type(encoding):
2
+ if (encoding == 'Raw'):
3
+ return '.ATKIMAGE'
4
+ return '.jpg'
@@ -0,0 +1,21 @@
1
+ import hashlib
2
+ import json
3
+
4
+ def create_enforcement_hash(request_json, created_on):
5
+ data = {}
6
+ data['commandTypeId'] = request_json['commandTypeId']
7
+ data['controlGroupId'] = request_json['controlGroupId']
8
+ data['controlSiteId'] = request_json['controlSiteId']
9
+ data['numberOfImages'] = request_json['numberOfImages']
10
+ data['startDatetime'] = request_json['startDatetime']
11
+ data['stopDatetime'] = request_json['stopDatetime']
12
+ data['speedLimit'] = request_json['speedLimit']
13
+ data['photoLimit'] = request_json['photoLimit']
14
+ data['heavyVehicleSpeedLimit'] = request_json['heavyVehicleSpeedLimit']
15
+ data['heavyVehiclePhotoLimit'] = request_json['heavyVehiclePhotoLimit']
16
+ data['testSeries'] = request_json['testSeries']
17
+ data['comment'] = request_json['comment']
18
+ data['commandCreatedBy'] = request_json['commandCreatedBy']
19
+ data['commandCreatedOn'] = created_on
20
+ json_to_hash = json.dumps(data, sort_keys = True).encode("utf-8")
21
+ return hashlib.md5(json_to_hash).hexdigest()
@@ -0,0 +1,18 @@
1
+ from http import HTTPStatus
2
+ from atk_common.utils.datetime_utils import get_utc_date_time_str
3
+
4
+ def is_http_status_ok(status_code):
5
+ return status_code >= HTTPStatus.OK.value and status_code < HTTPStatus.MULTIPLE_CHOICES.value
6
+
7
+ def is_http_status_internal(status_code):
8
+ return status_code >= HTTPStatus.INTERNAL_SERVER_ERROR.value
9
+
10
+ def get_test_response(docker_container_data, component):
11
+ data = {}
12
+ data['utcDateTime'] = get_utc_date_time_str()
13
+ if docker_container_data is None:
14
+ data['containerData'] = None
15
+ data['component'] = component
16
+ else:
17
+ data['containerData'] = docker_container_data
18
+ return data
@@ -0,0 +1,20 @@
1
+ from atk_common.enums.response_status_type_enum import ResponseStatusType
2
+
3
+ def create_response(status, status_code, response_msg, http_headers=None, content_type='application/json'):
4
+ data = {}
5
+ data['status'] = status
6
+ data['statusCode'] = status_code
7
+ data['responseMsg'] = response_msg
8
+ data['httpHeaders'] = http_headers
9
+ data['contentType'] = content_type
10
+ return data
11
+
12
+ def is_response_ok(response):
13
+ return response['status'] == ResponseStatusType.OK
14
+
15
+ def is_response_http(response):
16
+ return response['status'] == ResponseStatusType.HTTP
17
+
18
+ def is_response_internal(response):
19
+ return response['status'] == ResponseStatusType.INTERNAL
20
+
@@ -0,0 +1,30 @@
1
+ import json
2
+ from http import HTTPStatus
3
+ from atk_common.classes.bo_logger import BoLogger
4
+ from atk_common.enums.response_status_type_enum import ResponseStatusType
5
+ from atk_common.utils.error_utils import get_message
6
+ from atk_common.utils.internal_response_utils import create_response
7
+
8
+ def decode_message(body, message, bo_logger: BoLogger):
9
+ import gzip
10
+ import msgpack
11
+ try:
12
+ content_encoding = message.headers.get('content_encoding')
13
+ if content_encoding is not None and content_encoding == 'gzip':
14
+ body = gzip.decompress(body)
15
+ if message.content_type is None or message.content_type == '':
16
+ return create_response(ResponseStatusType.OK, HTTPStatus.OK, body)
17
+ elif message.content_type == 'application/json':
18
+ return create_response(ResponseStatusType.OK, HTTPStatus.OK, body)
19
+ elif message.content_type == 'application/octet-stream':
20
+ return create_response(ResponseStatusType.OK, HTTPStatus.OK, body)
21
+ elif message.content_type == 'application/x-msgpack' or message.content_type == 'application/msgpack':
22
+ return create_response(ResponseStatusType.OK, HTTPStatus.OK, msgpack.unpackb(body, raw=False))
23
+ elif message.content_type.startswith('text/'):
24
+ return create_response(ResponseStatusType.OK, HTTPStatus.OK, body.decode('utf-8'))
25
+ else:
26
+ bo_logger.critical(f"Unknown message content type {message.content_type}. Cannot decode message.")
27
+ return create_response(ResponseStatusType.INTERNAL, HTTPStatus.INTERNAL_SERVER_ERROR, get_message(error))
28
+ except Exception as error:
29
+ bo_logger.critical('Error decoding message: ' + get_message(error))
30
+ return create_response(ResponseStatusType.INTERNAL, HTTPStatus.INTERNAL_SERVER_ERROR, get_message(error))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atk_common
3
- Version: 2.1.0
3
+ Version: 3.0.0
4
4
  Summary: ATK common methods
5
5
  Home-page: https://github.com/pypa/atk_common
6
6
  Author: Roger
@@ -1,5 +1,5 @@
1
- atk_common/__init__.py,sha256=YSwim0faSNsJ8G__eIc2QoD4P8KygxfQxF8Y2tgk_6Q,2465
2
- atk_common/bo_logger.py,sha256=Ac7Xzs24AWR6erYGgp8YZw5zNkSY1NSwbOZNq-OMrVU,1453
1
+ atk_common/__init__.py,sha256=c3aje-XkwrJBNF2NZnBV2SMj28J5F2Ha5Y86JwbPlcc,15
2
+ atk_common/bo_logger.py,sha256=lzaXwLO1REIbKoFIEa5hEZZXkF4wqTZV6wl4T5DF6uo,1583
3
3
  atk_common/consumer_retry_handler.py,sha256=H_s9COWmjuvbzcjWxe2XpxrwfZB0j1Ns6-NO5iaxFtM,2119
4
4
  atk_common/datetime_utils.py,sha256=0SC5-Nai4RJH9B0VzvGKUQts_QeRXGb7tJLlsh73LJw,3556
5
5
  atk_common/db_utils.py,sha256=odUtXcS7Mumw5eGyVyVimL_U_lP7TqMX9v8nWO5nMvg,902
@@ -9,13 +9,20 @@ atk_common/env_utils.py,sha256=VCXEaAEGWFdQr5x7pKmks1Xs_g6gyrcwikqSz2XfETo,813
9
9
  atk_common/error_utils.py,sha256=kO-VQlajD8SO9MBxj8MHZRTK_uBXgPa09hy6pfjqZBY,2622
10
10
  atk_common/file_utils.py,sha256=UDwcRquO9IrqRrlUM0t-_g4R1-FKt8ZqQinSEqXOAk8,112
11
11
  atk_common/hash_utils.py,sha256=S_9o89CdI4lUQbVaqc85TDcqyDNuo30_E3VBaOrZKko,1047
12
- atk_common/http_response_utils.py,sha256=xS3d3AkfpNDkvRRGSfdGhSaIR6MM0ZIC5N9ZoJs4cNo,1794
12
+ atk_common/http_response_utils.py,sha256=pjTBoWceRowsgmtazkm2iI1oTpR8ips9mwXqJu6dnH4,1800
13
13
  atk_common/http_utils.py,sha256=Av6wMa9984zQqXSrU3jndxLQO1xwv9U7YDFvJqZQAuo,664
14
14
  atk_common/internal_response_utils.py,sha256=2X9eLFEy1pO3Aesj1IRXg2yprwNcBDM5_dXaA5vfmMI,694
15
15
  atk_common/log_utils.py,sha256=tw6Ph8oTpxrGYe_BcDTYkgrYmFAb1IxTXTodqctAIiY,504
16
- atk_common/mq_utils.py,sha256=IDni2Y2AVezsJGTwPY9QPZ95b8RT8osBKHhbYcWbp_U,1131
16
+ atk_common/mq_utils.py,sha256=6z4l7LsZWCzldsCZPsWnCtN4lIQ3gyoSuywQoQOh5Ak,1137
17
17
  atk_common/rabbitmq_consumer.py,sha256=4MhuwZs47Jt1fX4sUxr1MKRe7o2QRbPe9_utXEsa8QE,1907
18
18
  atk_common/response_utils.py,sha256=AxlmwkFoDU5XcFOzBQiuZxAQgswihpKXHSo1T0JJw3Q,556
19
+ atk_common/classes/__init__.py,sha256=O_VHYxAilmoz3i9L6jkwS-JZ4UaTezdhFiA5liNl1lY,532
20
+ atk_common/classes/bo_logger.py,sha256=DKEVtdm_ikMT3fbEEyWS3zsWvKn86Cckby2lTBm1mkM,2088
21
+ atk_common/classes/docker_handler.py,sha256=73WiebIT_RwGgwyvIed_Q5p8Tsb1b22JChBDyUmf1IU,2949
22
+ atk_common/classes/env_handler.py,sha256=sFx5dNiZZVOfIdvahOiDUG5LdHLIz4gAqGQGz2dqVgo,1286
23
+ atk_common/classes/error_handler.py,sha256=PrQ3njxYrqVCJzwdS2cSyshNktT_AHFMMRaC9vKtjyg,3257
24
+ atk_common/classes/http_response_handler.py,sha256=B83UW-W6pE5BcUaoYcIb1RS-TzZEeZ22vgWmvB7xT30,2176
25
+ atk_common/classes/rabbitmq_consumer.py,sha256=aPMF5H50k__a1HEy8GGwNdhSqu-wjbw2L3TESyd1cEg,2941
19
26
  atk_common/enums/__init__.py,sha256=hOSoKWIBUpRFaMN2tNJiel6iGI1MHj229OYnU1J8Jg0,2636
20
27
  atk_common/enums/api_error_type_enum.py,sha256=9oW6ZaZ3lhMwR8r2sVNWGliS9C_jV-otiOYdezAuTp0,91
21
28
  atk_common/enums/camera_cabinet_type_enum.py,sha256=U2NVrsTCBgaMRwYJamnjshAW8Y7xlOVjvUzakdgVH9A,90
@@ -56,8 +63,25 @@ atk_common/enums/speed_control_status_type_enum.py,sha256=qpURh0K1L1tSpbrzVnckoe
56
63
  atk_common/enums/speed_control_stop_reason.py,sha256=pvLS6fpDhsCiIDAmiQBsHctxZnq-Dl2BOgJOxQnT5Hc,200
57
64
  atk_common/enums/test_image_type_enum.py,sha256=HUjxJorehnzRXMNF2uHk2DrAJ3Y_ajQvp0jW-mtlOhU,140
58
65
  atk_common/enums/violation_type_enum.py,sha256=01qTHOj-O8bOc-nwIHVnxLosm4cusD_YuqXM3ZsiRRk,86
59
- atk_common-2.1.0.dist-info/licenses/license.txt,sha256=_0O6fWM00-wTurDjnZhUP_N5QiwGhItaQZqHq5eqadA,1063
60
- atk_package/__init__.py,sha256=NcsmwFadivgIeWV0-5ACZxqmfo4EzTkJX0r4N6DFAdg,820
66
+ atk_common/interfaces/__init__.py,sha256=HynEg28Uy3msO7qd__VxajTasSe9-Evpj9yi3Uw2NTo,508
67
+ atk_common/interfaces/docker_handler_interface.py,sha256=aCJ6cELRlrbZC-sB-RQEpKhChCMEiYNclcfr62YjSCo,411
68
+ atk_common/interfaces/env_handler_interface.py,sha256=yrmtTplH5tnuOAs7CW5RtzLMTE5q9sdzTYjiS2naNQQ,300
69
+ atk_common/interfaces/error_handler_interface.py,sha256=H43hSeeThT_7EbNzh-rygAJWNAmyzFetodiXdmqKshU,522
70
+ atk_common/interfaces/http_response_handler_interface.py,sha256=QjDmhVj4AnUyoRtSHk_sfFPI-cto6TV7Mx2_87tiOrs,185
71
+ atk_common/interfaces/logger_interface.py,sha256=w9bhheJFCQ76qpoO_o0N-F6KVKf4HaHOKLRx_c7Jgsk,315
72
+ atk_common/utils/__init__.py,sha256=HPqGmoNy6y1Bgu3Um_o1g0R5DnS-UZv1_ueC4i0uP30,1708
73
+ atk_common/utils/consumer_retry_handler.py,sha256=TxlcX7JzU7XBXxGrpWHyisrpouItU0cLisVgTwO0boc,2601
74
+ atk_common/utils/datetime_utils.py,sha256=0SC5-Nai4RJH9B0VzvGKUQts_QeRXGb7tJLlsh73LJw,3556
75
+ atk_common/utils/db_utils.py,sha256=odUtXcS7Mumw5eGyVyVimL_U_lP7TqMX9v8nWO5nMvg,902
76
+ atk_common/utils/default_should_retry.py,sha256=qghFbU71ygC8ARc0jkbDlxwZtwEPUqO8vGhIhGJX-Ao,838
77
+ atk_common/utils/error_utils.py,sha256=i6AAW7g0CJJYRgnyruoTI6jujuC2vAH5938fb4mjeCw,133
78
+ atk_common/utils/file_utils.py,sha256=UDwcRquO9IrqRrlUM0t-_g4R1-FKt8ZqQinSEqXOAk8,112
79
+ atk_common/utils/hash_utils.py,sha256=S_9o89CdI4lUQbVaqc85TDcqyDNuo30_E3VBaOrZKko,1047
80
+ atk_common/utils/http_utils.py,sha256=eSRuQeDgN0ISQdByZqE6cIGXoorcAXz7PEtVntHUKAo,670
81
+ atk_common/utils/internal_response_utils.py,sha256=2X9eLFEy1pO3Aesj1IRXg2yprwNcBDM5_dXaA5vfmMI,694
82
+ atk_common/utils/mq_utils.py,sha256=W4EVbM9FMPEwk7QJGksTqPN_of3yL1kBIhWpQNi6v_I,1859
83
+ atk_common-3.0.0.dist-info/licenses/license.txt,sha256=_0O6fWM00-wTurDjnZhUP_N5QiwGhItaQZqHq5eqadA,1063
84
+ atk_package/__init__.py,sha256=okIFEefQhQrw6DZg6oCEVWsEdkVCk-57VXBW0IUG_wU,834
61
85
  atk_package/datetime_utils.py,sha256=qsVF7l90P1-xukG2tV_jLqG9J_Yfl5wTpyfrdPBlyMo,239
62
86
  atk_package/env_utils.py,sha256=bXOrxM3fZUslqfmZt75iphbEJHbG4riJa8XOVzPwIII,313
63
87
  atk_package/error_utils.py,sha256=C-1GPo4hXHd0reW1gMEqyTjLMt257adGDjdYIyftiRE,1430
@@ -69,7 +93,7 @@ atk_package/enums/__init__.py,sha256=EtUr_--MQj1Rc_R0sF_ELXIThmhpfmhDWq3YaK9oQMk
69
93
  atk_package/enums/command_status_enum.py,sha256=M2Nln27a_DbzI07-gfytWQk2X087JhkU6Fmard5qVHs,127
70
94
  atk_package/enums/speed_control_status_enum.py,sha256=qpURh0K1L1tSpbrzVnckoe4hUn1illIkbo7k4mLfzIM,182
71
95
  shared_python_atk_enforcement/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
- atk_common-2.1.0.dist-info/METADATA,sha256=bNqGgMP6r26lVwpyb-ABHeLbAQ0oFedLpwI8emqz7ms,1760
73
- atk_common-2.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
74
- atk_common-2.1.0.dist-info/top_level.txt,sha256=4CwRjkLnheIdI4jQwc4tK3dbRc58WqUmoqjkdDTWlME,41
75
- atk_common-2.1.0.dist-info/RECORD,,
96
+ atk_common-3.0.0.dist-info/METADATA,sha256=axS2RZluWJzOwXZT_YcnbIez7lAmxGLaXjEvVmaE-80,1760
97
+ atk_common-3.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
98
+ atk_common-3.0.0.dist-info/top_level.txt,sha256=4CwRjkLnheIdI4jQwc4tK3dbRc58WqUmoqjkdDTWlME,41
99
+ atk_common-3.0.0.dist-info/RECORD,,
atk_package/__init__.py CHANGED
@@ -1,10 +1,10 @@
1
1
  # __init__.py
2
2
  from atk_common.datetime_utils import date_time_utc, get_utc_date_time
3
3
  from atk_common.env_utils import get_env_value
4
- from atk_common.error_utils import get_message, get_error_entity, handle_error, get_response_error, get_error_type
4
+ from atk_common.utils.error_utils import get_message, get_error_entity, handle_error, get_response_error, get_error_type
5
5
  from atk_common.http_utils import is_status_code_ok
6
6
  from atk_common.log_utils import add_log_item, add_log_item_http
7
- from atk_common.rabbitmq_consumer import RabbitMQConsumer
7
+ from atk_common.classes.rabbitmq_consumer import RabbitMQConsumer
8
8
  from atk_common.internal_response_utils import create_save_resp
9
9
 
10
10
  __all__ = [