python-databases 0.1.4__py3-none-any.whl → 0.1.5__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.
- python_databases/elasticsearch_infrastructure/__init__.py +6 -1
- python_databases/elasticsearch_infrastructure/elasticsearch.py +108 -142
- {python_databases-0.1.4.dist-info → python_databases-0.1.5.dist-info}/METADATA +1 -1
- {python_databases-0.1.4.dist-info → python_databases-0.1.5.dist-info}/RECORD +7 -7
- {python_databases-0.1.4.dist-info → python_databases-0.1.5.dist-info}/WHEEL +0 -0
- {python_databases-0.1.4.dist-info → python_databases-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {python_databases-0.1.4.dist-info → python_databases-0.1.5.dist-info}/top_level.txt +0 -0
|
@@ -9,4 +9,9 @@ from python_databases.elasticsearch_infrastructure.elasticsearch import (
|
|
|
9
9
|
|
|
10
10
|
load_dotenv()
|
|
11
11
|
|
|
12
|
-
__all__ = [
|
|
12
|
+
__all__ = [
|
|
13
|
+
'UrlProtocol',
|
|
14
|
+
'ElasticSearch',
|
|
15
|
+
'ElasticSearchOnPrem',
|
|
16
|
+
'ElasticSearchCloud'
|
|
17
|
+
]
|
|
@@ -18,13 +18,13 @@ class UrlProtocol(Enum):
|
|
|
18
18
|
|
|
19
19
|
class ElasticSearch(ABC):
|
|
20
20
|
def __init__(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
self,
|
|
22
|
+
elk_hostname: str,
|
|
23
|
+
elasticsearch_port: Optional[int],
|
|
24
|
+
kibana_port: Optional[int],
|
|
25
|
+
protocol: UrlProtocol,
|
|
26
|
+
username: Optional[str],
|
|
27
|
+
password: Optional[str]
|
|
28
28
|
):
|
|
29
29
|
self.logger = get_logger(self.__class__.__name__)
|
|
30
30
|
self._change_elasticsearch_logger()
|
|
@@ -43,177 +43,136 @@ class ElasticSearch(ABC):
|
|
|
43
43
|
|
|
44
44
|
self.elk_client = None
|
|
45
45
|
|
|
46
|
-
def _change_elasticsearch_logger(self):
|
|
47
|
-
tracer = get_logger('elasticsearch')
|
|
48
|
-
tracer.setLevel(logging.CRITICAL) # or desired level
|
|
49
|
-
# tracer.addHandler(logging.FileHandler('indexer.log'))
|
|
50
|
-
|
|
51
46
|
@abstractmethod
|
|
52
47
|
def connect_to_elasticsearch(self):
|
|
53
48
|
pass
|
|
54
49
|
|
|
55
|
-
|
|
50
|
+
@staticmethod
|
|
51
|
+
def _change_elasticsearch_logger(debug_level: int = logging.CRITICAL) -> None:
|
|
52
|
+
tracer = get_logger('elasticsearch')
|
|
53
|
+
tracer.setLevel(debug_level)
|
|
54
|
+
|
|
55
|
+
def _set_list_of_docs_quick(self, list_of_docs: list[dict], request_timeout: int = 600) -> None:
|
|
56
|
+
if failed_response := helpers.streaming_bulk(
|
|
57
|
+
self.elk_client,
|
|
58
|
+
list_of_docs,
|
|
59
|
+
raise_on_error=False,
|
|
60
|
+
request_timeout=request_timeout,
|
|
61
|
+
chunk_size=1000
|
|
62
|
+
):
|
|
63
|
+
for item in failed_response:
|
|
64
|
+
if item[1]['index']['status'] != 201 or item[1]['index']['_shards']['failed'] > 0:
|
|
65
|
+
self.logger.debug(f'Failed document: \n{item}')
|
|
66
|
+
|
|
67
|
+
def _set_list_of_docs_safe(self, list_of_docs: list[dict]) -> None:
|
|
68
|
+
success_response, failed_response = helpers.bulk(self.elk_client, list_of_docs)
|
|
69
|
+
if success_response == len(list_of_docs):
|
|
70
|
+
self.logger.info('All documents were reported successfully')
|
|
71
|
+
else:
|
|
72
|
+
self.logger.error(
|
|
73
|
+
f'Not all documents were reported successfully.\n'
|
|
74
|
+
f'The documents that were not reported successfully: {failed_response}'
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def post_list_of_docs(self, list_of_docs: list[dict], request_timeout: int = 600, quick: bool = False) -> None:
|
|
56
78
|
self.logger.info('Start to report the documents to ELK')
|
|
57
79
|
|
|
58
80
|
if quick:
|
|
59
|
-
|
|
60
|
-
self.elk_client,
|
|
61
|
-
list_of_docs,
|
|
62
|
-
raise_on_error=False,
|
|
63
|
-
request_timeout=request_timeout,
|
|
64
|
-
chunk_size=1000
|
|
65
|
-
):
|
|
66
|
-
for item in failed_response:
|
|
67
|
-
if item[1]['index']['status'] != 201 or item[1]['index']['_shards']['failed'] > 0:
|
|
68
|
-
self.logger.debug(f'Failed document: \n{item}')
|
|
81
|
+
self._set_list_of_docs_quick(list_of_docs=list_of_docs, request_timeout=request_timeout)
|
|
69
82
|
else:
|
|
70
|
-
|
|
71
|
-
if success_response == len(list_of_docs):
|
|
72
|
-
self.logger.info('All documents were reported successfully')
|
|
73
|
-
else:
|
|
74
|
-
self.logger.error(
|
|
75
|
-
f'Not all documents were reported successfully.\n'
|
|
76
|
-
f'The documents that were not reported successfully: {failed_response}'
|
|
77
|
-
)
|
|
83
|
+
self._set_list_of_docs_safe(list_of_docs=list_of_docs)
|
|
78
84
|
self.logger.info('Finish to report the documents to ELK')
|
|
79
85
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"_index": index_name,
|
|
89
|
-
"_source": deepcopy(doc)
|
|
90
|
-
}
|
|
91
|
-
)
|
|
86
|
+
@staticmethod
|
|
87
|
+
def _get_doc_with_basic_info() -> dict:
|
|
88
|
+
doc = {}
|
|
89
|
+
|
|
90
|
+
timestamp = datetime.now(tz=timezone.utc)
|
|
91
|
+
date_and_time_str = timestamp.isoformat()
|
|
92
|
+
date = date_and_time_str.split('T')[0]
|
|
93
|
+
time_part = date_and_time_str.split('T')[1].split('+')[0]
|
|
92
94
|
|
|
93
|
-
def _add_basic_info(
|
|
94
|
-
self,
|
|
95
|
-
doc: dict,
|
|
96
|
-
doc_id: str,
|
|
97
|
-
timestamp: datetime,
|
|
98
|
-
date_and_time: str,
|
|
99
|
-
username: str = None
|
|
100
|
-
) -> dict:
|
|
101
95
|
doc.update(
|
|
102
96
|
{
|
|
103
|
-
'doc_id':
|
|
97
|
+
'doc_id': timestamp.strftime('%Y%m%dT%H%M%S%f'),
|
|
104
98
|
'timestamp': timestamp,
|
|
105
|
-
'
|
|
99
|
+
'date_and_time_str': date_and_time_str,
|
|
100
|
+
'date_str': date,
|
|
101
|
+
'time_str': time_part,
|
|
106
102
|
}
|
|
107
103
|
)
|
|
108
|
-
if username:
|
|
109
|
-
doc['username'] = username
|
|
110
104
|
return doc
|
|
111
105
|
|
|
112
|
-
|
|
106
|
+
@staticmethod
|
|
107
|
+
def _add_list_values_as_str(doc: dict, column: str, value: Any) -> None:
|
|
113
108
|
"""Add list values as both list and string representation to the provided doc"""
|
|
114
109
|
if type(value) is list and all(type(item) is str for item in value):
|
|
115
110
|
filtered_value = list(filter(lambda item: item is not None, value))
|
|
116
111
|
doc[f'{column}'] = filtered_value
|
|
117
112
|
doc[f'{column}_str'] = ', '.join(filtered_value)
|
|
118
113
|
|
|
119
|
-
def _build_document(
|
|
120
|
-
|
|
121
|
-
row: dict,
|
|
122
|
-
doc_id: str,
|
|
123
|
-
timestamp: datetime,
|
|
124
|
-
date_and_time: str,
|
|
125
|
-
username: str = None
|
|
126
|
-
) -> dict:
|
|
127
|
-
doc = self._add_basic_info(
|
|
128
|
-
doc={},
|
|
129
|
-
doc_id=doc_id,
|
|
130
|
-
timestamp=timestamp,
|
|
131
|
-
date_and_time=date_and_time,
|
|
132
|
-
username=username
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
# Add custom fields
|
|
114
|
+
def _build_document(self, row: dict) -> dict:
|
|
115
|
+
doc = self._get_doc_with_basic_info()
|
|
136
116
|
for custom_field, value in row.items():
|
|
137
117
|
doc[custom_field] = value
|
|
118
|
+
|
|
138
119
|
self._add_list_values_as_str(
|
|
139
120
|
doc=doc,
|
|
121
|
+
column=custom_field,
|
|
140
122
|
value=value,
|
|
141
|
-
column=custom_field
|
|
142
123
|
)
|
|
143
|
-
|
|
144
124
|
return doc
|
|
145
125
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
) -> list[dict]:
|
|
126
|
+
@staticmethod
|
|
127
|
+
def _fill_list_of_docs(index_name: str, doc: dict[str, Any]) -> dict:
|
|
128
|
+
return {
|
|
129
|
+
"_index": index_name,
|
|
130
|
+
"_source": deepcopy(doc)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
def _prepare_documents_for_bulk(self, data: list[dict], doc_index_name: str) -> list[dict]:
|
|
155
134
|
list_of_docs = []
|
|
156
135
|
for index, row in enumerate(data):
|
|
157
136
|
self.logger.debug(f'index: {index + 1}/{len(data)}')
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
doc_id=doc_id,
|
|
162
|
-
timestamp=timestamp,
|
|
163
|
-
date_and_time=date_and_time,
|
|
164
|
-
username=username
|
|
137
|
+
doc = self._build_document(row=row)
|
|
138
|
+
list_of_docs.append(
|
|
139
|
+
self._fill_list_of_docs(index_name=doc_index_name, doc=doc)
|
|
165
140
|
)
|
|
166
|
-
|
|
167
|
-
self.fill_list_of_docs(
|
|
168
|
-
list_of_docs=list_of_docs,
|
|
169
|
-
index_name=doc_index_name,
|
|
170
|
-
doc=doc
|
|
171
|
-
)
|
|
172
|
-
|
|
173
141
|
return list_of_docs
|
|
174
142
|
|
|
175
|
-
def
|
|
143
|
+
def post_list_of_docs_as_bulk_chunk(
|
|
144
|
+
self,
|
|
145
|
+
list_of_docs: list[dict],
|
|
146
|
+
chunk_size=1000,
|
|
147
|
+
time_sleep: int = 60,
|
|
148
|
+
quick: bool = False
|
|
149
|
+
) -> None:
|
|
176
150
|
for i in range(0, len(list_of_docs), chunk_size):
|
|
177
151
|
self.logger.info(f'index: {i} - {i + chunk_size} / {len(list_of_docs)}')
|
|
178
152
|
chunk = list_of_docs[i:i + chunk_size]
|
|
179
|
-
self.
|
|
153
|
+
self.post_list_of_docs(list_of_docs=chunk, quick=quick)
|
|
180
154
|
time.sleep(time_sleep)
|
|
181
155
|
|
|
182
|
-
def fill_elk_index_as_bulk(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
username: Optional[str] = None,
|
|
190
|
-
chunk_size=1000,
|
|
191
|
-
time_sleep: int = 1
|
|
156
|
+
def fill_elk_index_as_bulk( # post_list_of_docs_as_bulk
|
|
157
|
+
self,
|
|
158
|
+
data: list[dict],
|
|
159
|
+
doc_index_name: str,
|
|
160
|
+
chunk_size=1000,
|
|
161
|
+
time_sleep: int = 1,
|
|
162
|
+
quick: bool = True
|
|
192
163
|
) -> None: # use as the main function to send data to the elastic search
|
|
193
|
-
timestamp = timestamp or datetime.now(tz=timezone.utc)
|
|
194
164
|
list_of_docs = self._prepare_documents_for_bulk(
|
|
195
165
|
data=data,
|
|
196
166
|
doc_index_name=doc_index_name,
|
|
197
|
-
timestamp=timestamp,
|
|
198
|
-
doc_id=doc_id or timestamp.strftime('%Y_%m_%dT%H_%M_%S_%f'),
|
|
199
|
-
date_and_time=date_and_time or timestamp.isoformat(),
|
|
200
|
-
username=username
|
|
201
167
|
)
|
|
202
168
|
|
|
203
|
-
self.
|
|
169
|
+
self.post_list_of_docs_as_bulk_chunk(
|
|
204
170
|
list_of_docs=list_of_docs,
|
|
205
171
|
chunk_size=chunk_size,
|
|
206
|
-
time_sleep=time_sleep
|
|
172
|
+
time_sleep=time_sleep,
|
|
173
|
+
quick=quick,
|
|
207
174
|
)
|
|
208
175
|
|
|
209
|
-
def convert_dataframes_to_list_of_docs(self, dataframe: pd.DataFrame) -> list:
|
|
210
|
-
data = []
|
|
211
|
-
dataframe_values = dataframe.values.tolist()
|
|
212
|
-
for index, value in enumerate(dataframe_values):
|
|
213
|
-
self.logger.debug(f'index: {index}/{len(dataframe_values)}')
|
|
214
|
-
data.append(value)
|
|
215
|
-
return data
|
|
216
|
-
|
|
217
176
|
def check_if_index_exists(self, index: str) -> bool:
|
|
218
177
|
if self.elk_client.indices.exists(index=index):
|
|
219
178
|
self.logger.info(f"The index '{index}' exists.")
|
|
@@ -231,7 +190,6 @@ class ElasticSearch(ABC):
|
|
|
231
190
|
self.delete_index(index=index)
|
|
232
191
|
return True
|
|
233
192
|
else:
|
|
234
|
-
self.logger.info(f"The index '{index}' does not exist.")
|
|
235
193
|
return False
|
|
236
194
|
|
|
237
195
|
def get_documents(self, index: str, query: dict) -> list[dict]:
|
|
@@ -241,16 +199,24 @@ class ElasticSearch(ABC):
|
|
|
241
199
|
except Exception as e:
|
|
242
200
|
raise Exception(f"Failed to get documents from index '{index}': {str(e)}")
|
|
243
201
|
|
|
202
|
+
def convert_dataframes_to_list_of_docs(self, dataframe: pd.DataFrame) -> list:
|
|
203
|
+
data = []
|
|
204
|
+
dataframe_values = dataframe.values.tolist()
|
|
205
|
+
for index, value in enumerate(dataframe_values):
|
|
206
|
+
self.logger.debug(f'index: {index}/{len(dataframe_values)}')
|
|
207
|
+
data.append(value)
|
|
208
|
+
return data
|
|
209
|
+
|
|
244
210
|
|
|
245
211
|
class ElasticSearchOnPrem(ElasticSearch):
|
|
246
212
|
def __init__(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
213
|
+
self,
|
|
214
|
+
elk_hostname: str,
|
|
215
|
+
elasticsearch_port: Optional[int] = 9200,
|
|
216
|
+
kibana_port: Optional[int] = 5602,
|
|
217
|
+
protocol: UrlProtocol = UrlProtocol.HTTPS,
|
|
218
|
+
username: Optional[str] = None,
|
|
219
|
+
password: Optional[str] = None
|
|
254
220
|
):
|
|
255
221
|
super().__init__(
|
|
256
222
|
elk_hostname=elk_hostname,
|
|
@@ -272,10 +238,10 @@ class ElasticSearchOnPrem(ElasticSearch):
|
|
|
272
238
|
hosts=[self.elasticsearch_url],
|
|
273
239
|
http_auth=(self.username, self.password),
|
|
274
240
|
# ca_certs="/etc/elasticsearch/certs/http_ca.crt",
|
|
275
|
-
verify_certs=False
|
|
241
|
+
verify_certs=False
|
|
276
242
|
)
|
|
277
243
|
else:
|
|
278
|
-
self.elk_client = Elasticsearch(hosts=[f'{self.elasticsearch_url}']
|
|
244
|
+
self.elk_client = Elasticsearch(hosts=[f'{self.elasticsearch_url}'])
|
|
279
245
|
|
|
280
246
|
if self.elk_client.ping():
|
|
281
247
|
self.logger.info("Elasticsearch on-prem Connection Successful")
|
|
@@ -287,13 +253,13 @@ class ElasticSearchOnPrem(ElasticSearch):
|
|
|
287
253
|
|
|
288
254
|
class ElasticSearchCloud(ElasticSearch):
|
|
289
255
|
def __init__(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
256
|
+
self,
|
|
257
|
+
elk_hostname: str,
|
|
258
|
+
elasticsearch_port: Optional[int] = 9200,
|
|
259
|
+
kibana_port: Optional[int] = 5602,
|
|
260
|
+
protocol: UrlProtocol = UrlProtocol.HTTPS,
|
|
261
|
+
username: Optional[str] = None,
|
|
262
|
+
password: Optional[str] = None
|
|
297
263
|
):
|
|
298
264
|
super().__init__(
|
|
299
265
|
elk_hostname=elk_hostname,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
python_databases/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
python_databases/elasticsearch_infrastructure/__init__.py,sha256=
|
|
3
|
-
python_databases/elasticsearch_infrastructure/elasticsearch.py,sha256=
|
|
2
|
+
python_databases/elasticsearch_infrastructure/__init__.py,sha256=cjs95C1yBZpc3kEdkcXEamDvQGsO6ob5r5ttFCJfDaA,314
|
|
3
|
+
python_databases/elasticsearch_infrastructure/elasticsearch.py,sha256=A5NwzqMIcAyuMLhVIlbpoIYO18jQFqWmtaZ7XyeLzzw,10259
|
|
4
4
|
python_databases/postgresql_infrastructure/__init__.py,sha256=h1eJx0ogbtcF6UnN2c7uW5iCuadDab3So1VI15y3kWA,150
|
|
5
5
|
python_databases/postgresql_infrastructure/postgresql.py,sha256=zH8y5w2k2djhHLefDJhlGwa6MtoZKONhJ5vMxIxDdlc,2856
|
|
6
|
-
python_databases-0.1.
|
|
7
|
-
python_databases-0.1.
|
|
8
|
-
python_databases-0.1.
|
|
9
|
-
python_databases-0.1.
|
|
10
|
-
python_databases-0.1.
|
|
6
|
+
python_databases-0.1.5.dist-info/licenses/LICENSE,sha256=cSikHY6SZFsPZSBizCDAJ0-Bjjzxt-JtX6TVbKxwimo,1067
|
|
7
|
+
python_databases-0.1.5.dist-info/METADATA,sha256=SccVOpAUU_gPpKNiHgZO2DANGUgjFw7pGcpf59hgUiA,1792
|
|
8
|
+
python_databases-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
+
python_databases-0.1.5.dist-info/top_level.txt,sha256=fXcMUntaYJfKeFS7FjQ6DY4WVXYBsgdrDmhOudYs-vg,17
|
|
10
|
+
python_databases-0.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|