vavacars-data-utils 1.4.4__tar.gz → 1.5.3__tar.gz

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 (42) hide show
  1. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/PKG-INFO +2 -2
  2. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/setup.cfg +1 -1
  3. vavacars_data_utils-1.5.3/vava_utils/logging/__init__.py +1 -0
  4. vavacars_data_utils-1.5.3/vava_utils/logging/logging_middleware.py +152 -0
  5. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vavacars_data_utils.egg-info/PKG-INFO +2 -2
  6. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vavacars_data_utils.egg-info/SOURCES.txt +4 -0
  7. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/README.md +0 -0
  8. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/pyproject.toml +0 -0
  9. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/test/utils/__init__.py +0 -0
  10. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/test/utils/test_strings.py +0 -0
  11. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/__init__.py +0 -0
  12. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/azure/__init__.py +0 -0
  13. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/azure/az_helper.py +0 -0
  14. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/bigquery/__init__.py +0 -0
  15. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/bigquery/bigquery_helper.py +0 -0
  16. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/camunda/__init__.py +0 -0
  17. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/camunda/camunda_helper.py +0 -0
  18. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/email/__init__.py +0 -0
  19. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/email/email_helper.py +0 -0
  20. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/loganalytics/__init__.py +0 -0
  21. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/loganalytics/loganalytics_helper.py +0 -0
  22. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/salesforce/__init__.py +0 -0
  23. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/salesforce/salesforce_helper.py +0 -0
  24. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/smartiq/__init__.py +0 -0
  25. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/smartiq/smartiq_helper.py +0 -0
  26. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/smartiq/smartiq_helper_v2.py +0 -0
  27. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/sql/__init__.py +0 -0
  28. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/sql/sql_helper.py +0 -0
  29. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/turkey/__init__.py +0 -0
  30. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/turkey/dates.py +0 -0
  31. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/utils/__init__.py +0 -0
  32. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/utils/callables.py +0 -0
  33. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/utils/comparisons.py +0 -0
  34. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/utils/naming.py +0 -0
  35. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/utils/singleton.py +0 -0
  36. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/utils/strings.py +0 -0
  37. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/utils/utils.py +0 -0
  38. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/vavaprice/__init__.py +0 -0
  39. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vava_utils/vavaprice/vavaprice_helper.py +0 -0
  40. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vavacars_data_utils.egg-info/dependency_links.txt +0 -0
  41. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vavacars_data_utils.egg-info/requires.txt +0 -0
  42. {vavacars_data_utils-1.4.4 → vavacars_data_utils-1.5.3}/vavacars_data_utils.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: vavacars_data_utils
3
- Version: 1.4.4
3
+ Version: 1.5.3
4
4
  Summary: Package with utils
5
5
  Home-page: https://dev.azure.com/vavacars/DataScience/_git/Data.Utils
6
6
  Author: Vavacars Data Science Team
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = vavacars_data_utils
3
- version = 1.4.4
3
+ version = 1.5.3
4
4
  author = Vavacars Data Science Team
5
5
  author_email = acg@vava.cars
6
6
  description = Package with utils
@@ -0,0 +1 @@
1
+ from .logging_middleware import LoggingMiddleware
@@ -0,0 +1,152 @@
1
+ from fastapi import Request, Response
2
+ from starlette.middleware.base import BaseHTTPMiddleware
3
+ from starlette.responses import JSONResponse
4
+ from starlette.background import BackgroundTask
5
+ import logging
6
+ import json
7
+ import re
8
+ from typing import Any, Callable, List, Tuple, Union
9
+
10
+ DEFAULT_MAX_LOG_SIZE = 10000
11
+ DEFAULT_LOG_FORMAT = "PATH: {path} | METHOD: {method} | STATUS: {status_code} | REQUEST: {req} | RESPONSE: {res}"
12
+ ALLOWED_MEDIA_TYPES = {None, "application/json", "text/plain", "text/html"}
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ def create_path_matcher(pattern: str) -> Callable[[str], bool]:
17
+ """
18
+ Creates a path matcher function from a glob-style pattern:
19
+ - '*' matches any single segment (i.e. any run of chars except '.')
20
+ - matches either exactly or with any number of leading segments
21
+ """
22
+ # Escape dots, then turn '*' into “any run of non-dot chars”
23
+ regex_body = re.escape(pattern).replace(r"\*", "[^.]*")
24
+ # Allow an optional “anything.” prefix
25
+ full_regex = re.compile(rf"^(?:.*\.)?{regex_body}$")
26
+ return lambda path: bool(full_regex.match(path))
27
+
28
+
29
+ DEFAULT_PATTERN_MAP = [
30
+ # ("similarVehicles.*.vehicles", lambda lst: {"size": len(lst)}),
31
+ # ("password", lambda _: "[REMOVED]"),
32
+ ("similarVehicles.*.vehicles", lambda lst: {"size": len(lst), "first_element": lst[0] if lst else None}),
33
+ ]
34
+
35
+
36
+ class LoggingMiddleware(BaseHTTPMiddleware):
37
+ def __init__(
38
+ self,
39
+ app,
40
+ log_format: Union[str, Callable] = DEFAULT_LOG_FORMAT,
41
+ max_log_size: int = DEFAULT_MAX_LOG_SIZE,
42
+ pattern_map: List[Tuple[str, Callable]] = [],
43
+ ):
44
+ super().__init__(app)
45
+ self.max_log_size = max_log_size
46
+ self.log_format = log_format
47
+
48
+ self.summary_map = []
49
+ for pattern, summary_fn in pattern_map:
50
+ matcher_fn = create_path_matcher(pattern)
51
+ self.summary_map.append((matcher_fn, summary_fn))
52
+
53
+ def _format_log(self, path, method, status_code, req, res):
54
+ """Format the log message according to the specified format."""
55
+ # If a custom formatter function is provided, use it
56
+ if callable(self.log_format):
57
+ return self.log_format(path=path, method=method, status_code=status_code, request=req, response=res)
58
+ else: # is string
59
+ return self.log_format.format(path=path, method=method, status_code=status_code, request=req, response=res)
60
+
61
+ def _process(self, data: bytes) -> str:
62
+ """
63
+ Decode JSON, apply summaries, truncate by size, fallback to one-line.
64
+ """
65
+ text = data.decode("utf-8", errors="ignore")
66
+ try:
67
+ payload = json.loads(text)
68
+
69
+ def apply(node: Any, path: List[str] = []) -> Any:
70
+ if not isinstance(node, (dict, list)):
71
+ return node
72
+ path = path or []
73
+ path_str = ".".join(path)
74
+
75
+ for pattern_fn, summary_fn in self.summary_map:
76
+ if pattern_fn(path_str):
77
+ return summary_fn(node)
78
+
79
+ if isinstance(node, dict):
80
+ return {k: apply(v, path + [k]) for k, v in node.items()}
81
+ if isinstance(node, list):
82
+ return [apply(v, path + ["*"]) for v in node]
83
+ return node
84
+
85
+ summarized = apply(payload)
86
+ dumped = json.dumps(summarized, ensure_ascii=False)
87
+ if len(dumped) > self.max_log_size:
88
+ return f"[json] payload too large ({len(dumped)} chars)"
89
+ return dumped
90
+ except Exception:
91
+ text = text.replace("\r", " ").replace("\n", " ").replace("\t", " ")
92
+ if len(text) > self.max_log_size:
93
+ return text[: self.max_log_size] + "…"
94
+ return text
95
+
96
+ def _log_info(
97
+ self,
98
+ method: str,
99
+ path: str,
100
+ req_bytes: bytes,
101
+ res_bytes: bytes,
102
+ status_code: int,
103
+ media_type: str,
104
+ ):
105
+ if method.upper() == "OPTIONS":
106
+ return
107
+
108
+ req = self._process(req_bytes)
109
+ if media_type in ALLOWED_MEDIA_TYPES:
110
+ res = self._process(res_bytes)
111
+ else:
112
+ res = f"[{media_type}] - Not logged"
113
+
114
+ # Use the custom formatter
115
+ msg = self._format_log(path, method, status_code, req, res)
116
+
117
+ if 200 <= status_code < 300:
118
+ logger.info(msg)
119
+ elif 400 <= status_code < 500:
120
+ logger.warning(msg)
121
+ else:
122
+ logger.error(msg)
123
+
124
+ async def dispatch(self, request: Request, call_next):
125
+ req_body = await request.body()
126
+ try:
127
+ response = await call_next(request)
128
+ body = b""
129
+ async for chunk in response.body_iterator:
130
+ body += chunk
131
+
132
+ response = Response(
133
+ content=body,
134
+ status_code=response.status_code,
135
+ headers=dict(response.headers),
136
+ media_type=response.media_type,
137
+ )
138
+ except Exception as e:
139
+ body = str(e).encode("utf-8")
140
+ response = JSONResponse(content={"detail": str(e)}, status_code=500)
141
+ raise
142
+ finally:
143
+ response.background = BackgroundTask(
144
+ self._log_info,
145
+ request.method,
146
+ request.url.path,
147
+ req_body,
148
+ body,
149
+ response.status_code,
150
+ response.media_type,
151
+ )
152
+ return response
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: vavacars_data_utils
3
- Version: 1.4.4
3
+ Version: 1.5.3
4
4
  Summary: Package with utils
5
5
  Home-page: https://dev.azure.com/vavacars/DataScience/_git/Data.Utils
6
6
  Author: Vavacars Data Science Team
@@ -14,6 +14,8 @@ setup.cfg
14
14
  ./vava_utils/email/email_helper.py
15
15
  ./vava_utils/loganalytics/__init__.py
16
16
  ./vava_utils/loganalytics/loganalytics_helper.py
17
+ ./vava_utils/logging/__init__.py
18
+ ./vava_utils/logging/logging_middleware.py
17
19
  ./vava_utils/salesforce/__init__.py
18
20
  ./vava_utils/salesforce/salesforce_helper.py
19
21
  ./vava_utils/smartiq/__init__.py
@@ -45,6 +47,8 @@ vava_utils/email/__init__.py
45
47
  vava_utils/email/email_helper.py
46
48
  vava_utils/loganalytics/__init__.py
47
49
  vava_utils/loganalytics/loganalytics_helper.py
50
+ vava_utils/logging/__init__.py
51
+ vava_utils/logging/logging_middleware.py
48
52
  vava_utils/salesforce/__init__.py
49
53
  vava_utils/salesforce/salesforce_helper.py
50
54
  vava_utils/smartiq/__init__.py