IservETLLib 0.0.1__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.
- iservetllib-0.0.1/LICENSE +0 -0
- iservetllib-0.0.1/PKG-INFO +33 -0
- iservetllib-0.0.1/README.md +20 -0
- iservetllib-0.0.1/pyproject.toml +20 -0
- iservetllib-0.0.1/setup.cfg +4 -0
- iservetllib-0.0.1/src/IservETLLib/DatabaseManager.py +134 -0
- iservetllib-0.0.1/src/IservETLLib/Helpers.py +17 -0
- iservetllib-0.0.1/src/IservETLLib/LogManager.py +80 -0
- iservetllib-0.0.1/src/IservETLLib/__init__.py +2 -0
- iservetllib-0.0.1/src/IservETLLib/index.py +26 -0
- iservetllib-0.0.1/src/IservETLLib.egg-info/PKG-INFO +33 -0
- iservetllib-0.0.1/src/IservETLLib.egg-info/SOURCES.txt +12 -0
- iservetllib-0.0.1/src/IservETLLib.egg-info/dependency_links.txt +1 -0
- iservetllib-0.0.1/src/IservETLLib.egg-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: IservETLLib
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Общий код для ETL jupiter notebooks
|
|
5
|
+
Author-email: k-avtonomov <k-avtonomov@it-serv.ru>, a-kostyuhin <a-kostyuhin@it-serv.ru>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# **Библиотека для ETL** #
|
|
15
|
+
|
|
16
|
+
Для формирования xlsx отчета из нескольких csv файлов.
|
|
17
|
+
|
|
18
|
+
## Разработка ##
|
|
19
|
+
|
|
20
|
+
source venv/bin/activate
|
|
21
|
+
|
|
22
|
+
## Установка ##
|
|
23
|
+
|
|
24
|
+
pip install IservETLLib
|
|
25
|
+
|
|
26
|
+
## Использование ##
|
|
27
|
+
|
|
28
|
+
python3 ./src/IservETLLib/index.py
|
|
29
|
+
|
|
30
|
+
<!-- python3 OUTPUT_NAME="res.xlsx" HEADERS_PATH="" META_NAME="excel.meta" SEP=; "Опросный лист ТУ_2025-07-28_14-28-44.csv" "Опросный лист УСПД_2025-07-28_14-33-04.csv" -->
|
|
31
|
+
|
|
32
|
+
python3 -m build
|
|
33
|
+
python3 -m twine upload --repository pypi dist/*
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# **Библиотека для ETL** #
|
|
2
|
+
|
|
3
|
+
Для формирования xlsx отчета из нескольких csv файлов.
|
|
4
|
+
|
|
5
|
+
## Разработка ##
|
|
6
|
+
|
|
7
|
+
source venv/bin/activate
|
|
8
|
+
|
|
9
|
+
## Установка ##
|
|
10
|
+
|
|
11
|
+
pip install IservETLLib
|
|
12
|
+
|
|
13
|
+
## Использование ##
|
|
14
|
+
|
|
15
|
+
python3 ./src/IservETLLib/index.py
|
|
16
|
+
|
|
17
|
+
<!-- python3 OUTPUT_NAME="res.xlsx" HEADERS_PATH="" META_NAME="excel.meta" SEP=; "Опросный лист ТУ_2025-07-28_14-28-44.csv" "Опросный лист УСПД_2025-07-28_14-33-04.csv" -->
|
|
18
|
+
|
|
19
|
+
python3 -m build
|
|
20
|
+
python3 -m twine upload --repository pypi dist/*
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 77.0.3"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "IservETLLib"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="k-avtonomov", email="k-avtonomov@it-serv.ru" },
|
|
10
|
+
{ name="a-kostyuhin", email="a-kostyuhin@it-serv.ru" },
|
|
11
|
+
]
|
|
12
|
+
description = "Общий код для ETL jupiter notebooks"
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
license = "MIT"
|
|
20
|
+
license-files = ["LICEN[CS]E*"]
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from sqlalchemy import create_engine, text, URL
|
|
3
|
+
from sqlalchemy.exc import SQLAlchemyError
|
|
4
|
+
|
|
5
|
+
class DBConfig:
|
|
6
|
+
def __init__(self, host, port, user, password, database):
|
|
7
|
+
self.host = host
|
|
8
|
+
self.port = port
|
|
9
|
+
self.user = user
|
|
10
|
+
self.password = password
|
|
11
|
+
self.database = database
|
|
12
|
+
|
|
13
|
+
def __str__(self):
|
|
14
|
+
return f"DatabaseConfig(host={self.host}, port={self.port}, user={self.user}, database={self.database})"
|
|
15
|
+
|
|
16
|
+
def __repr__(self):
|
|
17
|
+
return self.__str__()
|
|
18
|
+
|
|
19
|
+
class DBManager:
|
|
20
|
+
def __init__(self, application_name, config_string, logger):
|
|
21
|
+
self.db_config = self.parse_db_config(config_string)
|
|
22
|
+
self.url_object = URL.create(
|
|
23
|
+
drivername="postgresql",
|
|
24
|
+
username=self.db_config.user,
|
|
25
|
+
password=self.db_config.password,
|
|
26
|
+
host=self.db_config.host,
|
|
27
|
+
database=self.db_config.database,
|
|
28
|
+
)
|
|
29
|
+
self.application_name = application_name
|
|
30
|
+
if logger:
|
|
31
|
+
self.logger = logger
|
|
32
|
+
|
|
33
|
+
def parse_db_config(self, config_string):
|
|
34
|
+
"""Парсит строку конфигурации создает DatabaseConfig"""
|
|
35
|
+
config_dict = {}
|
|
36
|
+
|
|
37
|
+
# Разделяем по точкам с запятой
|
|
38
|
+
pairs = config_string.split(';')
|
|
39
|
+
|
|
40
|
+
for pair in pairs:
|
|
41
|
+
if ':' in pair:
|
|
42
|
+
key, value = pair.split(':', 1) # split только по первому двоеточию
|
|
43
|
+
config_dict[key.strip()] = value.strip()
|
|
44
|
+
|
|
45
|
+
# Создаем объект конфигурации
|
|
46
|
+
return DBConfig(
|
|
47
|
+
host=config_dict.get('host', ''),
|
|
48
|
+
port=int(config_dict.get('port', 0)),
|
|
49
|
+
user=config_dict.get('user', ''),
|
|
50
|
+
password=config_dict.get('password', ''),
|
|
51
|
+
database=config_dict.get('database', '')
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def query_database(self, connection_string, query, queryParams):
|
|
55
|
+
try:
|
|
56
|
+
engine = create_engine(connection_string,
|
|
57
|
+
connect_args={
|
|
58
|
+
"application_name": self.application_name
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
with engine.connect() as conn:
|
|
63
|
+
# Выполнение запроса с параметрами
|
|
64
|
+
result = conn.execute(query, queryParams)
|
|
65
|
+
#print("[query_database]", query, queryParams)
|
|
66
|
+
# Получение всех результатов
|
|
67
|
+
items = result.fetchall()
|
|
68
|
+
|
|
69
|
+
conn.commit()
|
|
70
|
+
return items
|
|
71
|
+
|
|
72
|
+
except SQLAlchemyError as e:
|
|
73
|
+
error_msg = f"Database error: {e}"
|
|
74
|
+
self.logger.log(f'{error_msg} {query} {queryParams}')
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
finally:
|
|
78
|
+
# Гарантированное отключение от БД
|
|
79
|
+
if 'engine' in locals():
|
|
80
|
+
engine.dispose()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def of_users(sender_id):
|
|
84
|
+
query = text("""
|
|
85
|
+
SELECT * FROM core.of_users(
|
|
86
|
+
(SELECT to_jsonb(t) FROM core.sf_users(:sender_id) as t),
|
|
87
|
+
'{"c_profiles":["user", "brigade"]}'::jsonb
|
|
88
|
+
);
|
|
89
|
+
"""
|
|
90
|
+
)
|
|
91
|
+
queryParams = {
|
|
92
|
+
"sender_id": sender_id
|
|
93
|
+
}
|
|
94
|
+
return query, queryParams
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def of_levels(sender_id):
|
|
98
|
+
query = text("""
|
|
99
|
+
SELECT * FROM core.of_levels(
|
|
100
|
+
(SELECT to_jsonb(t) FROM core.sf_users(:sender_id) as t),
|
|
101
|
+
null
|
|
102
|
+
);
|
|
103
|
+
"""
|
|
104
|
+
)
|
|
105
|
+
queryParams = {
|
|
106
|
+
"sender_id": sender_id
|
|
107
|
+
}
|
|
108
|
+
return query, queryParams
|
|
109
|
+
|
|
110
|
+
def cs_event_types():
|
|
111
|
+
query = text("""
|
|
112
|
+
SELECT id, c_name, c_const, b_disabled, b_default
|
|
113
|
+
FROM dbo.cs_event_types;
|
|
114
|
+
"""
|
|
115
|
+
)
|
|
116
|
+
queryParams = {}
|
|
117
|
+
return query, queryParams
|
|
118
|
+
|
|
119
|
+
def of_arm_cd_user_events(sender_id, d_date_start, d_date_end):
|
|
120
|
+
query = text("""
|
|
121
|
+
SELECT * FROM dbo.of_arm_cd_user_events(
|
|
122
|
+
(SELECT to_jsonb(t) FROM core.sf_users(:sender_id) as t),
|
|
123
|
+
:json_params
|
|
124
|
+
);
|
|
125
|
+
"""
|
|
126
|
+
)
|
|
127
|
+
queryParams = {
|
|
128
|
+
"sender_id": sender_id,
|
|
129
|
+
"json_params": json.dumps({
|
|
130
|
+
"d_date_start": d_date_start,
|
|
131
|
+
"d_date_end": d_date_end
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
return query, queryParams
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
class Helpers:
|
|
5
|
+
def generate_route_number(base_number):
|
|
6
|
+
"""
|
|
7
|
+
Генерирует номер маршрута в формате BASE-YYYY-MM-DD-LAST2DIGITS
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
base_number : int
|
|
12
|
+
Организационное подразделение пользователя
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
now = datetime.now()
|
|
16
|
+
timestamp_ms = int(time.time() * 1000)
|
|
17
|
+
return f"{base_number}-{now.strftime('%Y-%m-%d')}-{str(timestamp_ms)[-2:]}"
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import uuid
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from fluent import sender, event
|
|
6
|
+
|
|
7
|
+
class FluentConf:
|
|
8
|
+
def __init__(self, host, port):
|
|
9
|
+
self.host = host
|
|
10
|
+
self.port = port
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LogLevel(Enum):
|
|
14
|
+
INFO = 'INFO'
|
|
15
|
+
ERROR = 'ERROR'
|
|
16
|
+
CRITICAL = 'CRITICAL'
|
|
17
|
+
|
|
18
|
+
class Logger:
|
|
19
|
+
def __init__(self, application_name, file):
|
|
20
|
+
self.file = file
|
|
21
|
+
self.application_name = application_name
|
|
22
|
+
self.fluent = None
|
|
23
|
+
|
|
24
|
+
def init_fluent(self, level: LogLevel, fluent: FluentConf):
|
|
25
|
+
if not fluent:
|
|
26
|
+
raise Exception('Конфиг пустой')
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
sender.fluent = fluent
|
|
30
|
+
sender.setup(level, host=fluent.host, port=fluent.port)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def log(self, message):
|
|
34
|
+
self.__log(LogLevel.INFO, message)
|
|
35
|
+
|
|
36
|
+
def error(self, message, b_raise=True):
|
|
37
|
+
self.__log((LogLevel.CRITICAL if b_raise else LogLevel.ERROR), message)
|
|
38
|
+
|
|
39
|
+
def __log(self, level: LogLevel, message):
|
|
40
|
+
"""
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
level: LogLevel
|
|
44
|
+
уровень INFO ERROR CRITICAL
|
|
45
|
+
message : str
|
|
46
|
+
входная строка
|
|
47
|
+
|
|
48
|
+
Raises
|
|
49
|
+
-------
|
|
50
|
+
Exception
|
|
51
|
+
Исключение при записи в лог-файл
|
|
52
|
+
"""
|
|
53
|
+
datetime_string = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
54
|
+
message = f'{level.name} {datetime_string}: {message}'
|
|
55
|
+
print(message)
|
|
56
|
+
|
|
57
|
+
file = self.file
|
|
58
|
+
try:
|
|
59
|
+
# Создаем директорию, если она не существует
|
|
60
|
+
dir = os.path.dirname(file)
|
|
61
|
+
if dir: # Если путь содержит директорию
|
|
62
|
+
os.makedirs(dir, exist_ok=True)
|
|
63
|
+
|
|
64
|
+
with open(file, 'a', encoding='utf-8') as f:
|
|
65
|
+
f.write(message + '\n')
|
|
66
|
+
except Exception as e:
|
|
67
|
+
print(f'Ошибка записи в лог-файл {file}: {e}')
|
|
68
|
+
|
|
69
|
+
if self.fluent:
|
|
70
|
+
event.Event(level.name, {
|
|
71
|
+
"id": f"etl.{self.application_name}.{uuid.uuid4()}",
|
|
72
|
+
"@timestamp": datetime_string,
|
|
73
|
+
"message": message
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
if level == LogLevel.CRITICAL:
|
|
77
|
+
raise Exception(message)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
from DatabaseManager import DBManager
|
|
3
|
+
from LogManager import Logger, LogLevel, FluentConf
|
|
4
|
+
from Helpers import Helpers
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
CONNECTION_STRING = "host:192.168.17.46;port:5432;user:user;password:password;database:skr-dev-db"
|
|
8
|
+
ETL = "import_calendar.ipynb"
|
|
9
|
+
OUTPUT_DIR = "./files/output"
|
|
10
|
+
|
|
11
|
+
__CONNECTION_STRING=CONNECTION_STRING if 'CONNECTION_STRING' in locals() else None
|
|
12
|
+
__OUTPUT_DIR=OUTPUT_DIR if 'OUTPUT_DIR' in locals() else ''
|
|
13
|
+
__ETL=ETL if 'ETL' in locals() else 'unknown etl'
|
|
14
|
+
# __USR_ID=USR_ID if 'USR_ID' in locals() else 'null'
|
|
15
|
+
# # __FILEPATHNAME=FILEPATHNAME if 'FILEPATHNAME' in locals() else ''
|
|
16
|
+
# __FILEPATHNAME=os.path.join(OUTPUT_DIR.rsplit('/output', 1)[0], FILEPATHNAME) if 'FILEPATHNAME' in locals() else None #Используются на проде
|
|
17
|
+
__LOG_FILE = __OUTPUT_DIR + '/output.log'
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
|
|
22
|
+
logger = Logger(__ETL, __LOG_FILE)
|
|
23
|
+
dbm = DBManager(__ETL, __CONNECTION_STRING, logger)
|
|
24
|
+
# logger.init_fluent(LogLevel.INFO, FluentConf('10.199.236.8', 24224))
|
|
25
|
+
logger.log("asdas")
|
|
26
|
+
print(Helpers.generate_route_number('asdad'))
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: IservETLLib
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Общий код для ETL jupiter notebooks
|
|
5
|
+
Author-email: k-avtonomov <k-avtonomov@it-serv.ru>, a-kostyuhin <a-kostyuhin@it-serv.ru>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# **Библиотека для ETL** #
|
|
15
|
+
|
|
16
|
+
Для формирования xlsx отчета из нескольких csv файлов.
|
|
17
|
+
|
|
18
|
+
## Разработка ##
|
|
19
|
+
|
|
20
|
+
source venv/bin/activate
|
|
21
|
+
|
|
22
|
+
## Установка ##
|
|
23
|
+
|
|
24
|
+
pip install IservETLLib
|
|
25
|
+
|
|
26
|
+
## Использование ##
|
|
27
|
+
|
|
28
|
+
python3 ./src/IservETLLib/index.py
|
|
29
|
+
|
|
30
|
+
<!-- python3 OUTPUT_NAME="res.xlsx" HEADERS_PATH="" META_NAME="excel.meta" SEP=; "Опросный лист ТУ_2025-07-28_14-28-44.csv" "Опросный лист УСПД_2025-07-28_14-33-04.csv" -->
|
|
31
|
+
|
|
32
|
+
python3 -m build
|
|
33
|
+
python3 -m twine upload --repository pypi dist/*
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/IservETLLib/DatabaseManager.py
|
|
5
|
+
src/IservETLLib/Helpers.py
|
|
6
|
+
src/IservETLLib/LogManager.py
|
|
7
|
+
src/IservETLLib/__init__.py
|
|
8
|
+
src/IservETLLib/index.py
|
|
9
|
+
src/IservETLLib.egg-info/PKG-INFO
|
|
10
|
+
src/IservETLLib.egg-info/SOURCES.txt
|
|
11
|
+
src/IservETLLib.egg-info/dependency_links.txt
|
|
12
|
+
src/IservETLLib.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
IservETLLib
|