mbu-dev-shared-components 2.4.8__tar.gz → 3.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.
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/PKG-INFO +4 -1
- mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/database/__init__.py +4 -0
- mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/database/connection.py +49 -0
- mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/database/constants.py +54 -0
- mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/database/logging.py +139 -0
- mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/database/utility.py +109 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/document.py +0 -8
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components.egg-info/PKG-INFO +4 -1
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components.egg-info/SOURCES.txt +1 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components.egg-info/requires.txt +3 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/pyproject.toml +4 -5
- mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/database/constants.py +0 -131
- mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/database/logging.py +0 -86
- mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/database/utility.py +0 -44
- mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/utils/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/LICENSE +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/README.md +0 -0
- {mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/database → mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/getorganized}/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/getorganized/auth.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/getorganized/cases.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/getorganized/contacts.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/getorganized/documents.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/getorganized/objects.py +0 -0
- {mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/getorganized → mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/google}/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/google → mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/google/api}/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/google/api/auth.py +0 -0
- {mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/google/api → mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/google/workspace}/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/google/workspace/alerts.py +0 -0
- {mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/google/workspace → mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/msoffice365}/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/msoffice365 → mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/msoffice365/excel}/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/msoffice365/excel/excel_reader.py +0 -0
- {mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/msoffice365/excel → mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/msoffice365/sharepoint_api}/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/msoffice365/sharepoint_api/files.py +0 -0
- {mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/msoffice365/sharepoint_api → mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/os2forms}/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/os2forms/documents.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/os2forms/forms.py +0 -0
- {mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/os2forms → mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/romexis}/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/romexis/db_handler.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/romexis/helper_functions.py +0 -0
- {mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/romexis → mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/sap}/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/sap/create_invoice.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/app_handler.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/appointment.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/base_ui.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/clinic.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/edi_portal.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/event.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/exceptions.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/handler_base.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/journal_note.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/application/patient.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/database/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/solteqtand/database/db_handler.py +0 -0
- {mbu_dev_shared_components-2.4.8/mbu_dev_shared_components/sap → mbu_dev_shared_components-3.0.1/mbu_dev_shared_components/utils}/__init__.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/utils/db_stored_procedure_executor.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/utils/fernet_encryptor.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/utils/file_handler.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components/utils/json_handler.py +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components.egg-info/dependency_links.txt +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/mbu_dev_shared_components.egg-info/top_level.txt +0 -0
- {mbu_dev_shared_components-2.4.8 → mbu_dev_shared_components-3.0.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mbu_dev_shared_components
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.1
|
|
4
4
|
Summary: Shared components to use in RPA projects
|
|
5
5
|
Author-email: MBU <rpa@mbu.aarhus.dk>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -22,6 +22,9 @@ Requires-Dist: docx2pdf
|
|
|
22
22
|
Requires-Dist: pandas>=2.2.3
|
|
23
23
|
Requires-Dist: rawpy
|
|
24
24
|
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pylint; extra == "dev"
|
|
26
|
+
Requires-Dist: flake8; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-json-report; extra == "dev"
|
|
25
28
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
26
29
|
Requires-Dist: pytest-dependency>=0.5.1; extra == "dev"
|
|
27
30
|
Dynamic: license-file
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Handles the RPA connection"""
|
|
2
|
+
|
|
3
|
+
from .constants import Constants
|
|
4
|
+
from .utility import Utility
|
|
5
|
+
from .logging import Log
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RPAConnection(
|
|
9
|
+
Constants,
|
|
10
|
+
Utility,
|
|
11
|
+
Log,
|
|
12
|
+
):
|
|
13
|
+
"""Class for running database related """
|
|
14
|
+
def __init__(self, db_env: str = "PROD", commit: bool | str = False):
|
|
15
|
+
Constants.__init__(self)
|
|
16
|
+
Utility.__init__(self)
|
|
17
|
+
Log.__init__(self)
|
|
18
|
+
self.db_env = db_env
|
|
19
|
+
self.commit = commit if isinstance(commit, bool) else commit == "True"
|
|
20
|
+
self.conn = None
|
|
21
|
+
self.cursor = None
|
|
22
|
+
|
|
23
|
+
def __enter__(self):
|
|
24
|
+
self.conn = self.connect_to_db(autocommit=False, db_env=self.db_env)
|
|
25
|
+
self.cursor = self.conn.cursor()
|
|
26
|
+
|
|
27
|
+
return self
|
|
28
|
+
|
|
29
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
30
|
+
if self.commit:
|
|
31
|
+
print("Commiting transaction...")
|
|
32
|
+
self.conn.commit()
|
|
33
|
+
else:
|
|
34
|
+
print("Rolling back transaction....")
|
|
35
|
+
self.conn.rollback()
|
|
36
|
+
print("Closing conection...")
|
|
37
|
+
self.close()
|
|
38
|
+
print("Connection closed.")
|
|
39
|
+
|
|
40
|
+
def rollback(self):
|
|
41
|
+
"""Rollback transaction on connection if autocommit is not enabled"""
|
|
42
|
+
if self.autocommit:
|
|
43
|
+
raise RuntimeError("Cannot rollback: autocommit is enabled.")
|
|
44
|
+
self.conn.rollback()
|
|
45
|
+
|
|
46
|
+
def close(self):
|
|
47
|
+
"""Closes connection"""
|
|
48
|
+
self.cursor.close()
|
|
49
|
+
self.conn.close()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""This module handles generating and fetching constants and credentials from the database"""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
from mbu_dev_shared_components.utils.fernet_encryptor import Encryptor
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Constants:
|
|
9
|
+
"""Base class for adding and collection constants and credentials"""
|
|
10
|
+
|
|
11
|
+
def add_constant(self, constant_name: str, value: str, changed_at: datetime = datetime.now()):
|
|
12
|
+
query = """
|
|
13
|
+
INSERT INTO [RPA].[rpa].[Constants] ([name], [value], [changed_at])
|
|
14
|
+
VALUES (?, ?, ?)
|
|
15
|
+
"""
|
|
16
|
+
self.execute_query(query, [constant_name, value, changed_at])
|
|
17
|
+
|
|
18
|
+
def get_constant(self, constant_name: str) -> dict:
|
|
19
|
+
query = """
|
|
20
|
+
SELECT name, value FROM [RPA].[rpa].[Constants] WHERE name = ?
|
|
21
|
+
"""
|
|
22
|
+
res = self.execute_query(query, [constant_name])
|
|
23
|
+
if res:
|
|
24
|
+
name, value = res[0]
|
|
25
|
+
return {"constant_name": name, "value": value}
|
|
26
|
+
raise ValueError(f"No constant found with name: {constant_name}")
|
|
27
|
+
|
|
28
|
+
def add_credential(self, credential_name: str, username: str, password: str,
|
|
29
|
+
changed_at: datetime = datetime.now()):
|
|
30
|
+
encryptor = Encryptor()
|
|
31
|
+
encrypted_password = encryptor.encrypt(password)
|
|
32
|
+
query = """
|
|
33
|
+
INSERT INTO [RPA].[rpa].[Credentials] ([name], [username], [password], [changed_at])
|
|
34
|
+
VALUES (?, ?, ?, ?)
|
|
35
|
+
"""
|
|
36
|
+
self.execute_query(query, [credential_name, username, encrypted_password, changed_at])
|
|
37
|
+
|
|
38
|
+
def get_credential(self, credential_name: str) -> dict:
|
|
39
|
+
encryptor = Encryptor()
|
|
40
|
+
query = """
|
|
41
|
+
SELECT username, CAST(password AS varbinary(max))
|
|
42
|
+
FROM [RPA].[rpa].[Credentials]
|
|
43
|
+
WHERE name = ?
|
|
44
|
+
"""
|
|
45
|
+
res = self.execute_query(query, [credential_name])
|
|
46
|
+
if res:
|
|
47
|
+
username, encrypted_password = res[0]
|
|
48
|
+
decrypted_password = encryptor.decrypt(encrypted_password)
|
|
49
|
+
return {
|
|
50
|
+
"username": username,
|
|
51
|
+
"decrypted_password": decrypted_password,
|
|
52
|
+
"encrypted_password": encrypted_password
|
|
53
|
+
}
|
|
54
|
+
raise ValueError(f"No credential found with name {credential_name}")
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""This module handles logging in the RPA database"""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
import time
|
|
5
|
+
import socket
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Log:
|
|
9
|
+
"""Base class for handling logging"""
|
|
10
|
+
def log_event(
|
|
11
|
+
self,
|
|
12
|
+
log_db: str,
|
|
13
|
+
level: str,
|
|
14
|
+
message: str,
|
|
15
|
+
context: str,
|
|
16
|
+
):
|
|
17
|
+
"""Logs the inputted parameters in """
|
|
18
|
+
created_at = datetime.now()
|
|
19
|
+
query = f"""
|
|
20
|
+
INSERT INTO RPA.{log_db}
|
|
21
|
+
([level]
|
|
22
|
+
,[message]
|
|
23
|
+
,[created_at]
|
|
24
|
+
,[context])
|
|
25
|
+
VALUES
|
|
26
|
+
(?
|
|
27
|
+
,?
|
|
28
|
+
,?
|
|
29
|
+
,?)
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
params = [level, message, created_at, context]
|
|
33
|
+
self.execute_query(query=query, params=params)
|
|
34
|
+
|
|
35
|
+
def _get_log_event(
|
|
36
|
+
self,
|
|
37
|
+
log_db: str,
|
|
38
|
+
level: str,
|
|
39
|
+
message: str,
|
|
40
|
+
context: str,
|
|
41
|
+
):
|
|
42
|
+
query = f"""
|
|
43
|
+
SELECT
|
|
44
|
+
([level]
|
|
45
|
+
,[message]
|
|
46
|
+
,[created_at]
|
|
47
|
+
,[context])
|
|
48
|
+
FROM
|
|
49
|
+
RPA.{log_db}
|
|
50
|
+
WHERE
|
|
51
|
+
level={level},
|
|
52
|
+
message={message},
|
|
53
|
+
context={context}
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
res = self.execute_query(query=query)
|
|
57
|
+
return res
|
|
58
|
+
|
|
59
|
+
def get_latest_log(
|
|
60
|
+
self,
|
|
61
|
+
log_db: str,
|
|
62
|
+
):
|
|
63
|
+
"""Retrieve latest log message from database"""
|
|
64
|
+
query = f"""
|
|
65
|
+
SELECT TOP (1)
|
|
66
|
+
[level]
|
|
67
|
+
,[message]
|
|
68
|
+
,[created_at]
|
|
69
|
+
,[context]
|
|
70
|
+
FROM
|
|
71
|
+
RPA.{log_db}
|
|
72
|
+
ORDER BY
|
|
73
|
+
created_at desc
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
res = self.execute_query(query=query)
|
|
77
|
+
return res
|
|
78
|
+
|
|
79
|
+
def _send_heartbeat(
|
|
80
|
+
self,
|
|
81
|
+
servicename,
|
|
82
|
+
status,
|
|
83
|
+
details
|
|
84
|
+
):
|
|
85
|
+
"""Function to send heartbeat to database"""
|
|
86
|
+
hostname = socket.gethostname()
|
|
87
|
+
params = {
|
|
88
|
+
"ServiceName": (str, servicename),
|
|
89
|
+
"Status": (str, status),
|
|
90
|
+
"HostName": (str, hostname),
|
|
91
|
+
"Details": (str, details)
|
|
92
|
+
}
|
|
93
|
+
result = self.execute_stored_procedure(
|
|
94
|
+
stored_procedure='rpa.sp_UpdateHeartbeat',
|
|
95
|
+
params=params)
|
|
96
|
+
if result["success"] is not True:
|
|
97
|
+
print(result["error_message"])
|
|
98
|
+
|
|
99
|
+
def log_heartbeat(
|
|
100
|
+
self,
|
|
101
|
+
stop: str | bool,
|
|
102
|
+
servicename: str,
|
|
103
|
+
heartbeat_interval: int,
|
|
104
|
+
details: str = "",
|
|
105
|
+
):
|
|
106
|
+
"""Function to log heartbeat"""
|
|
107
|
+
# Update connection such that it autocommits
|
|
108
|
+
self.conn.autocommit = True # Sets class attribute
|
|
109
|
+
if isinstance(stop, str):
|
|
110
|
+
stop = stop == "True"
|
|
111
|
+
if not isinstance(heartbeat_interval, int):
|
|
112
|
+
heartbeat_interval = int(heartbeat_interval)
|
|
113
|
+
while not stop:
|
|
114
|
+
status = "RUNNING"
|
|
115
|
+
self._send_heartbeat(
|
|
116
|
+
servicename,
|
|
117
|
+
status,
|
|
118
|
+
details,
|
|
119
|
+
)
|
|
120
|
+
time.sleep(heartbeat_interval)
|
|
121
|
+
status = "STOPPED"
|
|
122
|
+
self._send_heartbeat(
|
|
123
|
+
servicename,
|
|
124
|
+
status,
|
|
125
|
+
details,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
def get_heartbeat(self, service_name: str):
|
|
129
|
+
"""Get hearbeats """
|
|
130
|
+
query = f"""
|
|
131
|
+
SELECT
|
|
132
|
+
*
|
|
133
|
+
FROM
|
|
134
|
+
[RPA].[rpa].[ServiceHeartbeat]
|
|
135
|
+
WHERE
|
|
136
|
+
ServiceName = '{service_name}'
|
|
137
|
+
"""
|
|
138
|
+
res = self.execute_query(query)
|
|
139
|
+
return res
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""This module handles general database connection and calls"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
from typing import Dict, Union, Tuple, Any
|
|
6
|
+
from dateutil import parser
|
|
7
|
+
import pyodbc
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Utility:
|
|
11
|
+
"""Base class handling general utilities"""
|
|
12
|
+
def connect_to_db(self, autocommit=True, db_env="PROD") -> pyodbc.Connection:
|
|
13
|
+
"""Establish connection to sql database
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
rpa_conn (pyodbc.Connection): The connection object to the SQL database.
|
|
17
|
+
"""
|
|
18
|
+
connection_env = self.fetch_env(db_env)
|
|
19
|
+
rpa_conn_string = os.getenv(connection_env)
|
|
20
|
+
rpa_conn = pyodbc.connect(rpa_conn_string, autocommit=autocommit)
|
|
21
|
+
return rpa_conn
|
|
22
|
+
|
|
23
|
+
def execute_query(self, query: str, params: list = None) -> pyodbc.Cursor:
|
|
24
|
+
"""Execute SQL query with pyodbc"""
|
|
25
|
+
params = [] if not params else params
|
|
26
|
+
is_select = query.strip().upper().startswith('SELECT')
|
|
27
|
+
try:
|
|
28
|
+
res = self.cursor.execute(query, params)
|
|
29
|
+
if is_select:
|
|
30
|
+
res = self.cursor.fetchall()
|
|
31
|
+
if len(res) == 0:
|
|
32
|
+
print("No results from query")
|
|
33
|
+
return None
|
|
34
|
+
return res
|
|
35
|
+
else:
|
|
36
|
+
return None
|
|
37
|
+
except pyodbc.Error as e:
|
|
38
|
+
print(e)
|
|
39
|
+
print(query)
|
|
40
|
+
raise e
|
|
41
|
+
|
|
42
|
+
def fetch_env(self, db_env):
|
|
43
|
+
"""Get env variable based on context, PROD or TEST"""
|
|
44
|
+
if db_env.upper() == "PROD":
|
|
45
|
+
connection_env = "DbConnectionString"
|
|
46
|
+
return connection_env
|
|
47
|
+
if db_env.upper() == "TEST":
|
|
48
|
+
connection_env = "DbConnectionStringTest"
|
|
49
|
+
return connection_env
|
|
50
|
+
|
|
51
|
+
raise ValueError(f"arg db_env is {db_env.upper()} but should be 'PROD' or 'TEST'")
|
|
52
|
+
|
|
53
|
+
def execute_stored_procedure(self, stored_procedure: str, params: Dict[str, Tuple[type, Any]] | None = None) -> Dict[str, Union[bool, str, Any]]:
|
|
54
|
+
"""
|
|
55
|
+
Executes a stored procedure with the given parameters.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
connection_string (str): The connection string to connect to the database.
|
|
59
|
+
stored_procedure (str): The name of the stored procedure to execute.
|
|
60
|
+
params (Dict[str, Tuple[type, Any]], optional): A dictionary of parameters to pass to the stored procedure.
|
|
61
|
+
Each value should be a tuple of (type, actual_value).
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Dict[str, Union[bool, str, Any]]: A dictionary containing the success status, an error message (if any),
|
|
65
|
+
number of affected rows, and additional data.
|
|
66
|
+
"""
|
|
67
|
+
result = {
|
|
68
|
+
"success": False,
|
|
69
|
+
"error_message": None,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type_mapping = {
|
|
73
|
+
"str": str,
|
|
74
|
+
"int": int,
|
|
75
|
+
"float": float,
|
|
76
|
+
"datetime": parser.isoparse,
|
|
77
|
+
"json": lambda x: json.dumps(x, ensure_ascii=False)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
if params:
|
|
82
|
+
param_placeholders = ', '.join([f"@{key} = ?" for key in params.keys()])
|
|
83
|
+
param_values = []
|
|
84
|
+
|
|
85
|
+
for key, value in params.items():
|
|
86
|
+
if isinstance(value, tuple) and len(value) == 2:
|
|
87
|
+
value_type, actual_value = value
|
|
88
|
+
if value_type in type_mapping:
|
|
89
|
+
param_values.append(type_mapping[value_type](actual_value))
|
|
90
|
+
else:
|
|
91
|
+
param_values.append(actual_value)
|
|
92
|
+
else:
|
|
93
|
+
raise ValueError("Each parameter value must be a tuple of (type, actual_value).")
|
|
94
|
+
|
|
95
|
+
sql = f"EXEC {stored_procedure} {param_placeholders}"
|
|
96
|
+
rows_updated = self.cursor.execute(sql, tuple(param_values))
|
|
97
|
+
else:
|
|
98
|
+
sql = f"EXEC {stored_procedure}"
|
|
99
|
+
rows_updated = self.cursor.execute(sql)
|
|
100
|
+
result["success"] = True
|
|
101
|
+
result["rows_updated"] = rows_updated.rowcount
|
|
102
|
+
except pyodbc.Error as e:
|
|
103
|
+
result["error_message"] = f"Database error: {str(e)}"
|
|
104
|
+
except ValueError as e:
|
|
105
|
+
result["error_message"] = f"Value error: {str(e)}"
|
|
106
|
+
except Exception as e:
|
|
107
|
+
result["error_message"] = f"An unexpected error occurred: {str(e)}"
|
|
108
|
+
|
|
109
|
+
return result
|
|
@@ -126,14 +126,6 @@ class DocumentHandler(HandlerBase):
|
|
|
126
126
|
first_booking = controls[0]
|
|
127
127
|
first_booking.RightClick(simulateMove=False, waitTime=0)
|
|
128
128
|
|
|
129
|
-
# list_bookings_group = self.wait_for_control(
|
|
130
|
-
# auto.GroupControl,
|
|
131
|
-
# {'AutomationId': 'GroupBoxView'},
|
|
132
|
-
# search_depth=13,
|
|
133
|
-
# )
|
|
134
|
-
# group_bookings_list = list_bookings_group.GetChildren()[0].GetChildren()[1]
|
|
135
|
-
# group_bookings_list.RightClick(simulateMove=False, waitTime=0)
|
|
136
|
-
|
|
137
129
|
pop_up_right_click_menu = self.wait_for_control(
|
|
138
130
|
auto.MenuControl,
|
|
139
131
|
{'Name': 'Kontekst'},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mbu_dev_shared_components
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.1
|
|
4
4
|
Summary: Shared components to use in RPA projects
|
|
5
5
|
Author-email: MBU <rpa@mbu.aarhus.dk>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -22,6 +22,9 @@ Requires-Dist: docx2pdf
|
|
|
22
22
|
Requires-Dist: pandas>=2.2.3
|
|
23
23
|
Requires-Dist: rawpy
|
|
24
24
|
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pylint; extra == "dev"
|
|
26
|
+
Requires-Dist: flake8; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-json-report; extra == "dev"
|
|
25
28
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
26
29
|
Requires-Dist: pytest-dependency>=0.5.1; extra == "dev"
|
|
27
30
|
Dynamic: license-file
|
|
@@ -7,6 +7,7 @@ mbu_dev_shared_components.egg-info/dependency_links.txt
|
|
|
7
7
|
mbu_dev_shared_components.egg-info/requires.txt
|
|
8
8
|
mbu_dev_shared_components.egg-info/top_level.txt
|
|
9
9
|
mbu_dev_shared_components/database/__init__.py
|
|
10
|
+
mbu_dev_shared_components/database/connection.py
|
|
10
11
|
mbu_dev_shared_components/database/constants.py
|
|
11
12
|
mbu_dev_shared_components/database/logging.py
|
|
12
13
|
mbu_dev_shared_components/database/utility.py
|
|
@@ -2,13 +2,9 @@
|
|
|
2
2
|
requires = ["setuptools>=65.0"]
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
|
-
#[tool.setuptools]
|
|
6
|
-
#package-dir = { "" = "." }
|
|
7
|
-
#packages = ["mbu_dev_shared_components"]
|
|
8
|
-
|
|
9
5
|
[project]
|
|
10
6
|
name = "mbu_dev_shared_components"
|
|
11
|
-
version = "
|
|
7
|
+
version = "3.0.1" # Specify the version manually here
|
|
12
8
|
authors = [
|
|
13
9
|
{ name="MBU", email="rpa@mbu.aarhus.dk" },
|
|
14
10
|
]
|
|
@@ -37,6 +33,9 @@ dependencies = [
|
|
|
37
33
|
|
|
38
34
|
[project.optional-dependencies]
|
|
39
35
|
dev = [
|
|
36
|
+
"pylint",
|
|
37
|
+
"flake8",
|
|
38
|
+
"pytest-json-report",
|
|
40
39
|
"pytest >= 7.0",
|
|
41
40
|
"pytest-dependency >= 0.5.1"
|
|
42
41
|
]
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
"""This module handles generating and fetching constants and credentials from the database"""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import pyodbc
|
|
5
|
-
from datetime import datetime
|
|
6
|
-
|
|
7
|
-
from mbu_dev_shared_components.utils.fernet_encryptor import Encryptor
|
|
8
|
-
from mbu_dev_shared_components.database.utility import connect_to_db, execute_query
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def add_credential(
|
|
12
|
-
credential_name: str,
|
|
13
|
-
username: str,
|
|
14
|
-
password: str,
|
|
15
|
-
changed_at: datetime = datetime.now(),
|
|
16
|
-
db_env: str = "PROD"
|
|
17
|
-
):
|
|
18
|
-
encryptor = Encryptor()
|
|
19
|
-
encrypted_password = encryptor.encrypt(password)
|
|
20
|
-
|
|
21
|
-
rpa_conn = connect_to_db(db_env=db_env)
|
|
22
|
-
cursor = rpa_conn.cursor()
|
|
23
|
-
|
|
24
|
-
query = """
|
|
25
|
-
INSERT INTO [RPA].[rpa].[Credentials]
|
|
26
|
-
([name]
|
|
27
|
-
,[username]
|
|
28
|
-
,[password]
|
|
29
|
-
,[changed_at])
|
|
30
|
-
VALUES
|
|
31
|
-
(?
|
|
32
|
-
,?
|
|
33
|
-
,?
|
|
34
|
-
,?)
|
|
35
|
-
"""
|
|
36
|
-
params = [credential_name, username, encrypted_password, changed_at]
|
|
37
|
-
execute_query(query=query, cursor=cursor, params=params)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def get_credential(
|
|
41
|
-
credential_name: str,
|
|
42
|
-
db_env: str = "PROD"
|
|
43
|
-
) -> dict:
|
|
44
|
-
|
|
45
|
-
rpa_conn = connect_to_db(db_env=db_env)
|
|
46
|
-
cursor = rpa_conn.cursor()
|
|
47
|
-
encryptor = Encryptor()
|
|
48
|
-
|
|
49
|
-
query = """
|
|
50
|
-
SELECT
|
|
51
|
-
Username
|
|
52
|
-
,cast(Password as varbinary(max))
|
|
53
|
-
FROM [RPA].[rpa].[Credentials]
|
|
54
|
-
WHERE
|
|
55
|
-
name = ?
|
|
56
|
-
"""
|
|
57
|
-
|
|
58
|
-
params = [credential_name]
|
|
59
|
-
|
|
60
|
-
res = execute_query(query=query, cursor=cursor, params=params)
|
|
61
|
-
if res is not None:
|
|
62
|
-
res = res[0]
|
|
63
|
-
username = res[0]
|
|
64
|
-
encrypted_password = res[1]
|
|
65
|
-
|
|
66
|
-
decrypted_password = encryptor.decrypt(encrypted_password)
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
"username": username,
|
|
70
|
-
"decrypted_password": decrypted_password,
|
|
71
|
-
"encrypted_password": encrypted_password
|
|
72
|
-
}
|
|
73
|
-
else:
|
|
74
|
-
print(f"No credential found with name {credential_name}")
|
|
75
|
-
|
|
76
|
-
def get_constant(
|
|
77
|
-
constant_name: str,
|
|
78
|
-
db_env: str = "PROD"
|
|
79
|
-
) -> tuple:
|
|
80
|
-
|
|
81
|
-
rpa_conn = connect_to_db(db_env=db_env)
|
|
82
|
-
cursor = rpa_conn.cursor()
|
|
83
|
-
encryptor = Encryptor()
|
|
84
|
-
|
|
85
|
-
query = """
|
|
86
|
-
SELECT
|
|
87
|
-
name
|
|
88
|
-
,value
|
|
89
|
-
FROM [RPA].[rpa].[Constants]
|
|
90
|
-
WHERE
|
|
91
|
-
name = ?
|
|
92
|
-
"""
|
|
93
|
-
|
|
94
|
-
params = [constant_name]
|
|
95
|
-
|
|
96
|
-
res = execute_query(query=query, cursor=cursor, params=params)
|
|
97
|
-
if res is not None:
|
|
98
|
-
|
|
99
|
-
returned_constant = res[0]
|
|
100
|
-
constant_name = returned_constant[0]
|
|
101
|
-
value = returned_constant[1]
|
|
102
|
-
|
|
103
|
-
return {"constant_name": constant_name, "value": value}
|
|
104
|
-
else:
|
|
105
|
-
print(f"No constant found with name: {constant_name}")
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def add_constant(
|
|
109
|
-
constant_name: str,
|
|
110
|
-
value: str,
|
|
111
|
-
changed_at: datetime = datetime.now(),
|
|
112
|
-
db_env: str = "PROD"
|
|
113
|
-
):
|
|
114
|
-
query = """
|
|
115
|
-
INSERT INTO [RPA].[rpa].[Constants]
|
|
116
|
-
([name]
|
|
117
|
-
,[value]
|
|
118
|
-
,[changed_at])
|
|
119
|
-
VALUES
|
|
120
|
-
(?
|
|
121
|
-
,?
|
|
122
|
-
,?)
|
|
123
|
-
"""
|
|
124
|
-
|
|
125
|
-
rpa_conn = connect_to_db(db_env=db_env)
|
|
126
|
-
cursor = rpa_conn.cursor()
|
|
127
|
-
|
|
128
|
-
params = [constant_name, value, changed_at]
|
|
129
|
-
execute_query(query=query, cursor=cursor, params=params)
|
|
130
|
-
|
|
131
|
-
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"""This module handles logging in the RPA database"""
|
|
2
|
-
|
|
3
|
-
from datetime import datetime
|
|
4
|
-
import time
|
|
5
|
-
import os
|
|
6
|
-
import socket
|
|
7
|
-
|
|
8
|
-
from mbu_dev_shared_components.database.utility import execute_query, connect_to_db, fetch_env
|
|
9
|
-
from mbu_dev_shared_components.utils.db_stored_procedure_executor import execute_stored_procedure
|
|
10
|
-
|
|
11
|
-
def log_event(
|
|
12
|
-
log_db: str,
|
|
13
|
-
level: str,
|
|
14
|
-
message: str,
|
|
15
|
-
context: str,
|
|
16
|
-
db_env="TEST"
|
|
17
|
-
):
|
|
18
|
-
created_at = datetime.now()
|
|
19
|
-
"""Logs the inputted parameters in """
|
|
20
|
-
query = f"""
|
|
21
|
-
INSERT INTO RPA.{log_db}
|
|
22
|
-
([level]
|
|
23
|
-
,[message]
|
|
24
|
-
,[created_at]
|
|
25
|
-
,[context])
|
|
26
|
-
VALUES
|
|
27
|
-
(?
|
|
28
|
-
,?
|
|
29
|
-
,?
|
|
30
|
-
,?)
|
|
31
|
-
"""
|
|
32
|
-
rpa_conn = connect_to_db(db_env=db_env)
|
|
33
|
-
cursor = rpa_conn.cursor()
|
|
34
|
-
|
|
35
|
-
params = [level, message, created_at, context]
|
|
36
|
-
execute_query(query=query, cursor=cursor, params=params)
|
|
37
|
-
|
|
38
|
-
def _send_heartbeat(
|
|
39
|
-
servicename,
|
|
40
|
-
status,
|
|
41
|
-
details,
|
|
42
|
-
db_env,
|
|
43
|
-
):
|
|
44
|
-
conn_env = fetch_env(db_env=db_env)
|
|
45
|
-
conn_str = os.getenv(conn_env)
|
|
46
|
-
hostname = socket.gethostname()
|
|
47
|
-
params = {
|
|
48
|
-
"ServiceName": (str, servicename),
|
|
49
|
-
"Status": (str, status),
|
|
50
|
-
"HostName": (str, hostname),
|
|
51
|
-
"Details": (str, details)
|
|
52
|
-
}
|
|
53
|
-
result = execute_stored_procedure(
|
|
54
|
-
connection_string=conn_str,
|
|
55
|
-
stored_procedure='rpa.sp_UpdateHeartbeat',
|
|
56
|
-
params=params)
|
|
57
|
-
if result["success"] is not True:
|
|
58
|
-
print(result["error_message"])
|
|
59
|
-
|
|
60
|
-
def log_heartbeat(
|
|
61
|
-
stop: str|bool,
|
|
62
|
-
servicename: str,
|
|
63
|
-
heartbeat_interval: int,
|
|
64
|
-
details: str = "",
|
|
65
|
-
db_env: str = "PROD"
|
|
66
|
-
):
|
|
67
|
-
if isinstance(stop,str):
|
|
68
|
-
stop = stop == "True"
|
|
69
|
-
if not isinstance(heartbeat_interval, int):
|
|
70
|
-
heartbeat_interval = int(heartbeat_interval)
|
|
71
|
-
while not stop:
|
|
72
|
-
status = "RUNNING"
|
|
73
|
-
_send_heartbeat(
|
|
74
|
-
servicename,
|
|
75
|
-
status,
|
|
76
|
-
details,
|
|
77
|
-
db_env,
|
|
78
|
-
)
|
|
79
|
-
time.sleep(heartbeat_interval)
|
|
80
|
-
status = "STOPPED"
|
|
81
|
-
_send_heartbeat(
|
|
82
|
-
servicename,
|
|
83
|
-
status,
|
|
84
|
-
details,
|
|
85
|
-
db_env,
|
|
86
|
-
)
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
"""This module handles general database connection and calls"""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import pyodbc
|
|
5
|
-
from datetime import datetime
|
|
6
|
-
|
|
7
|
-
def connect_to_db(autocommit=True, db_env="PROD") -> pyodbc.Connection:
|
|
8
|
-
"""Establish connection to sql database
|
|
9
|
-
|
|
10
|
-
Returns:
|
|
11
|
-
rpa_conn (pyodbc.Connection): The connection object to the SQL database.
|
|
12
|
-
"""
|
|
13
|
-
connection_env = fetch_env(db_env)
|
|
14
|
-
rpa_conn_string = os.getenv(connection_env)
|
|
15
|
-
rpa_conn = pyodbc.connect(rpa_conn_string, autocommit=autocommit)
|
|
16
|
-
return rpa_conn
|
|
17
|
-
|
|
18
|
-
def execute_query(query: str, cursor: pyodbc.Cursor, params: list) -> pyodbc.Cursor:
|
|
19
|
-
is_select = query.strip().upper().startswith('SELECT')
|
|
20
|
-
try:
|
|
21
|
-
res = cursor.execute(query, params)
|
|
22
|
-
if is_select:
|
|
23
|
-
res = cursor.fetchall()
|
|
24
|
-
if len(res) == 0:
|
|
25
|
-
print("No results from query")
|
|
26
|
-
return None
|
|
27
|
-
return res
|
|
28
|
-
else:
|
|
29
|
-
return None
|
|
30
|
-
except pyodbc.Error as e:
|
|
31
|
-
print(e)
|
|
32
|
-
finally:
|
|
33
|
-
cursor.close()
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def fetch_env(db_env):
|
|
37
|
-
if db_env.upper() == "PROD":
|
|
38
|
-
connection_env = "DbConnectionString"
|
|
39
|
-
return connection_env
|
|
40
|
-
if db_env.upper() == "TEST":
|
|
41
|
-
connection_env = "DbConnectionStringTest"
|
|
42
|
-
return connection_env
|
|
43
|
-
|
|
44
|
-
raise ValueError(f"arg db_env is {db_env.upper()} but should be 'PROD' or 'TEST'")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|