mbu-dev-shared-components 4.0.3__tar.gz → 4.2.0__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-4.0.3 → mbu_dev_shared_components-4.2.0}/PKG-INFO +4 -1
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/database/utility.py +30 -15
- mbu_dev_shared_components-4.2.0/mbu_dev_shared_components/solteqtand/application/aftalebog.py +209 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/app_handler.py +20 -20
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components.egg-info/PKG-INFO +4 -1
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components.egg-info/SOURCES.txt +1 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components.egg-info/requires.txt +3 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/pyproject.toml +4 -1
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/LICENSE +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/README.md +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/database/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/database/connection.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/database/constants.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/database/logging.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/getorganized/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/getorganized/auth.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/getorganized/cases.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/getorganized/contacts.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/getorganized/documents.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/getorganized/objects.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/google/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/google/api/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/google/api/auth.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/google/workspace/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/google/workspace/alerts.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/msoffice365/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/msoffice365/excel/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/msoffice365/excel/excel_reader.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/msoffice365/sharepoint_api/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/msoffice365/sharepoint_api/files.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/os2forms/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/os2forms/documents.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/os2forms/forms.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/romexis/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/romexis/db_handler.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/romexis/helper_functions.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/sap/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/sap/create_invoice.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/appointment.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/base_ui.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/clinic.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/document.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/edi_portal.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/event.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/exceptions.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/handler_base.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/journal_note.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/application/patient.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/database/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/solteqtand/database/db_handler.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/utils/__init__.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/utils/db_stored_procedure_executor.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/utils/fernet_encryptor.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/utils/file_handler.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components/utils/json_handler.py +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components.egg-info/dependency_links.txt +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/mbu_dev_shared_components.egg-info/top_level.txt +0 -0
- {mbu_dev_shared_components-4.0.3 → mbu_dev_shared_components-4.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mbu_dev_shared_components
|
|
3
|
-
Version: 4.0
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: Shared components to use in RPA projects
|
|
5
5
|
Author-email: MBU <rpa@mbu.aarhus.dk>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -12,6 +12,7 @@ License-File: LICENSE
|
|
|
12
12
|
Requires-Dist: cryptography>=43.0.0
|
|
13
13
|
Requires-Dist: pyodbc>=5.1.0
|
|
14
14
|
Requires-Dist: python-dateutil==2.9.*
|
|
15
|
+
Requires-Dist: dotenv
|
|
15
16
|
Provides-Extra: getorganized
|
|
16
17
|
Requires-Dist: requests_ntlm>=1.2.0; extra == "getorganized"
|
|
17
18
|
Provides-Extra: google
|
|
@@ -31,6 +32,8 @@ Provides-Extra: sap
|
|
|
31
32
|
Provides-Extra: solteqtand
|
|
32
33
|
Requires-Dist: uiautomation; extra == "solteqtand"
|
|
33
34
|
Requires-Dist: pyodbc>=5.1.0; extra == "solteqtand"
|
|
35
|
+
Requires-Dist: psutil; extra == "solteqtand"
|
|
36
|
+
Requires-Dist: docx2pdf; extra == "solteqtand"
|
|
34
37
|
Provides-Extra: utils
|
|
35
38
|
Requires-Dist: python-dateutil==2.9.*; extra == "utils"
|
|
36
39
|
Requires-Dist: cryptography>=43.0.0; extra == "utils"
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
"""This module handles general database connection and calls"""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
3
|
import json
|
|
5
|
-
|
|
6
|
-
from
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any, Dict, Tuple, Union
|
|
6
|
+
|
|
7
7
|
import pyodbc
|
|
8
|
-
from
|
|
8
|
+
from dateutil import parser
|
|
9
|
+
from dotenv import load_dotenv
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class Utility:
|
|
12
13
|
"""Base class handling general utilities"""
|
|
14
|
+
|
|
13
15
|
def connect_to_db(self, autocommit=True, db_env="PROD") -> pyodbc.Connection:
|
|
14
16
|
"""Establish connection to sql database
|
|
15
17
|
|
|
@@ -22,17 +24,24 @@ class Utility:
|
|
|
22
24
|
rpa_conn = pyodbc.connect(rpa_conn_string, autocommit=autocommit)
|
|
23
25
|
return rpa_conn
|
|
24
26
|
|
|
25
|
-
def execute_query(
|
|
27
|
+
def execute_query(
|
|
28
|
+
self, query: str, params: list = None, return_dict: bool = False
|
|
29
|
+
) -> list | None:
|
|
26
30
|
"""Execute SQL query with pyodbc"""
|
|
27
31
|
params = [] if not params else params
|
|
28
|
-
is_select = query.strip().upper().startswith(
|
|
32
|
+
is_select = query.strip().upper().startswith("SELECT")
|
|
29
33
|
try:
|
|
30
34
|
res = self.cursor.execute(query, params)
|
|
31
35
|
if is_select:
|
|
32
|
-
|
|
33
|
-
if len(
|
|
36
|
+
rows = self.cursor.fetchall()
|
|
37
|
+
if len(rows) == 0:
|
|
34
38
|
print("No results from query")
|
|
35
39
|
return None
|
|
40
|
+
if return_dict:
|
|
41
|
+
columns = [column[0] for column in self.cursor.description]
|
|
42
|
+
res = [dict(zip(columns, row)) for row in rows]
|
|
43
|
+
else:
|
|
44
|
+
res = rows
|
|
36
45
|
return res
|
|
37
46
|
else:
|
|
38
47
|
return None
|
|
@@ -44,15 +53,19 @@ class Utility:
|
|
|
44
53
|
def fetch_env(self, db_env):
|
|
45
54
|
"""Get env variable based on context, PROD or TEST"""
|
|
46
55
|
if db_env.upper() == "PROD":
|
|
47
|
-
connection_env = "DBCONNECTIONSTRINGPROD"
|
|
56
|
+
connection_env = "DBCONNECTIONSTRINGPROD"
|
|
48
57
|
return connection_env
|
|
49
58
|
if db_env.upper() == "TEST":
|
|
50
|
-
connection_env = "DBCONNECTIONSTRINGDEV"
|
|
59
|
+
connection_env = "DBCONNECTIONSTRINGDEV"
|
|
51
60
|
return connection_env
|
|
52
61
|
|
|
53
|
-
raise ValueError(
|
|
62
|
+
raise ValueError(
|
|
63
|
+
f"arg db_env is {db_env.upper()} but should be 'PROD' or 'TEST'"
|
|
64
|
+
)
|
|
54
65
|
|
|
55
|
-
def execute_stored_procedure(
|
|
66
|
+
def execute_stored_procedure(
|
|
67
|
+
self, stored_procedure: str, params: Dict[str, Tuple[type, Any]] | None = None
|
|
68
|
+
) -> Dict[str, Union[bool, str, Any]]:
|
|
56
69
|
"""
|
|
57
70
|
Executes a stored procedure with the given parameters.
|
|
58
71
|
|
|
@@ -76,12 +89,12 @@ class Utility:
|
|
|
76
89
|
"int": int,
|
|
77
90
|
"float": float,
|
|
78
91
|
"datetime": parser.isoparse,
|
|
79
|
-
"json": lambda x: json.dumps(x, ensure_ascii=False)
|
|
92
|
+
"json": lambda x: json.dumps(x, ensure_ascii=False),
|
|
80
93
|
}
|
|
81
94
|
|
|
82
95
|
try:
|
|
83
96
|
if params:
|
|
84
|
-
param_placeholders =
|
|
97
|
+
param_placeholders = ", ".join([f"@{key} = ?" for key in params.keys()])
|
|
85
98
|
param_values = []
|
|
86
99
|
|
|
87
100
|
for key, value in params.items():
|
|
@@ -92,7 +105,9 @@ class Utility:
|
|
|
92
105
|
else:
|
|
93
106
|
param_values.append(actual_value)
|
|
94
107
|
else:
|
|
95
|
-
raise ValueError(
|
|
108
|
+
raise ValueError(
|
|
109
|
+
"Each parameter value must be a tuple of (type, actual_value)."
|
|
110
|
+
)
|
|
96
111
|
|
|
97
112
|
sql = f"EXEC {stored_procedure} {param_placeholders}"
|
|
98
113
|
rows_updated = self.cursor.execute(sql, tuple(param_values))
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
import uiautomation as auto
|
|
4
|
+
|
|
5
|
+
from .handler_base import HandlerBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AftalebogHandler(HandlerBase):
|
|
9
|
+
"""Functions within aftalebog"""
|
|
10
|
+
|
|
11
|
+
def get_appointments_aftalebog(
|
|
12
|
+
self, close_after: bool = False, headers_to_keep: list | None = None
|
|
13
|
+
) -> dict:
|
|
14
|
+
"""Function to retrive data on appointments in view in aftalebog"""
|
|
15
|
+
|
|
16
|
+
# Get list control
|
|
17
|
+
list_box = self.wait_for_control(
|
|
18
|
+
control_type=auto.GroupControl,
|
|
19
|
+
search_params={"AutomationId": "GroupBoxView"},
|
|
20
|
+
search_depth=5,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
appointment_list = self.find_element_by_property(
|
|
24
|
+
control=list_box, control_type=50008
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Extract headers
|
|
28
|
+
appointment_headers = [
|
|
29
|
+
header.Name
|
|
30
|
+
for header in appointment_list.GetFirstChildControl().GetChildren()
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
# Extract ListItem controls
|
|
34
|
+
appointment_ctrls = [
|
|
35
|
+
ctrl for ctrl in appointment_list.GetChildren() if ctrl.ControlType == 50007
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# Package data in dictionary
|
|
39
|
+
# Keep only selected headers if any selected.
|
|
40
|
+
if not headers_to_keep:
|
|
41
|
+
headers_to_keep = appointment_headers
|
|
42
|
+
|
|
43
|
+
appointment_data = {
|
|
44
|
+
j: {
|
|
45
|
+
k: v.Name
|
|
46
|
+
for k, v in zip(appointment_headers, ctrl.GetChildren())
|
|
47
|
+
if k in headers_to_keep
|
|
48
|
+
}
|
|
49
|
+
for j, ctrl in enumerate(appointment_ctrls)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if close_after:
|
|
53
|
+
# Should maybe be in a method of its own?
|
|
54
|
+
list_box.SendKeys("{Control}{F4}")
|
|
55
|
+
self.wait_for_control_to_disappear(
|
|
56
|
+
control_type=auto.WindowControl,
|
|
57
|
+
search_params={"AutomationId": "FormBooking"},
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
return appointment_data
|
|
61
|
+
|
|
62
|
+
def set_date_in_aftalebog(self, from_date: datetime, to_date: datetime) -> None:
|
|
63
|
+
"""Set to and from dates in aftalebog oversigt"""
|
|
64
|
+
import locale
|
|
65
|
+
|
|
66
|
+
dt_picker_from = self.wait_for_control(
|
|
67
|
+
control_type=auto.PaneControl,
|
|
68
|
+
search_params={"AutomationId": "DateTimePickerFromDate"},
|
|
69
|
+
search_depth=7,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
from_keys = (
|
|
73
|
+
f"{from_date.day}"
|
|
74
|
+
+ "{right}"
|
|
75
|
+
+ f"{from_date.month}"
|
|
76
|
+
+ "{right}"
|
|
77
|
+
+ f"{from_date.year}"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
dt_picker_from.SendKeys(from_keys)
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
from_date.strftime(format="%d. %B %Y") == dt_picker_from.Name
|
|
84
|
+
except Exception:
|
|
85
|
+
# Should maybe try a number of times until it hits right or ends in systemerror
|
|
86
|
+
# End with raise error where resulting dates are printed
|
|
87
|
+
print("Dates after insert not matching input")
|
|
88
|
+
print(
|
|
89
|
+
(
|
|
90
|
+
f"'From' input: {from_date.strftime(format='%d. %B %Y')} "
|
|
91
|
+
+ f"Current value: {dt_picker_from.Name}"
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
dt_picker_to = self.wait_for_control(
|
|
96
|
+
control_type=auto.PaneControl,
|
|
97
|
+
search_params={"AutomationId": "DateTimePickerToDate"},
|
|
98
|
+
search_depth=7,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
to_keys = (
|
|
102
|
+
f"{to_date.day}"
|
|
103
|
+
+ "{right}"
|
|
104
|
+
+ f"{to_date.month}"
|
|
105
|
+
+ "{right}"
|
|
106
|
+
+ f"{to_date.year}"
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
dt_picker_to.SendKeys(to_keys)
|
|
110
|
+
|
|
111
|
+
locale.setlocale(locale.LC_TIME, "da_dk.utf-8")
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
to_date.strftime(format="%d. %B %Y") == dt_picker_to.Name
|
|
115
|
+
except Exception:
|
|
116
|
+
print("Dates after insert not matching input")
|
|
117
|
+
print(
|
|
118
|
+
(
|
|
119
|
+
f"'To' input: {to_date.strftime(format='%d. %B %Y')} "
|
|
120
|
+
+ f"Current value: {dt_picker_to.Name}"
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def pick_appointment_types_aftalebog(self, appointment_types: str | list):
|
|
125
|
+
"""Set one or more appointment types in aftalebog oversigt"""
|
|
126
|
+
|
|
127
|
+
if isinstance(appointment_types, str):
|
|
128
|
+
appointment_types = [appointment_types]
|
|
129
|
+
|
|
130
|
+
# deselect all
|
|
131
|
+
slct_none = self.wait_for_control(
|
|
132
|
+
control_type=auto.PaneControl,
|
|
133
|
+
search_params={"AutomationId": "ButtonToggleStatusList"},
|
|
134
|
+
search_depth=7,
|
|
135
|
+
)
|
|
136
|
+
slct_none.SetFocus()
|
|
137
|
+
# If possible to select none click once, otherwise click twice
|
|
138
|
+
try:
|
|
139
|
+
assert slct_none.Name == "Vælge ingen"
|
|
140
|
+
slct_none.SendKeys("{Enter}")
|
|
141
|
+
except AssertionError:
|
|
142
|
+
slct_none.SendKeys("{Enter}{Enter}")
|
|
143
|
+
|
|
144
|
+
# Getting status controls
|
|
145
|
+
status_list = self.wait_for_control(
|
|
146
|
+
control_type=auto.ListControl,
|
|
147
|
+
search_params={"AutomationId": "CheckedListBoxStatus"},
|
|
148
|
+
search_depth=7,
|
|
149
|
+
)
|
|
150
|
+
status_ctrls = [
|
|
151
|
+
_child
|
|
152
|
+
for _child in status_list.GetChildren()
|
|
153
|
+
if _child.ControlType == 50002
|
|
154
|
+
]
|
|
155
|
+
status_names = [
|
|
156
|
+
_child.Name
|
|
157
|
+
for _child in status_list.GetChildren()
|
|
158
|
+
if _child.ControlType == 50002
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
# Toggle all selected appointment types
|
|
162
|
+
for a_type in appointment_types:
|
|
163
|
+
slct_idx = status_names.index(a_type)
|
|
164
|
+
status_ctrls[slct_idx].GetTogglePattern().Toggle()
|
|
165
|
+
|
|
166
|
+
def pick_clinic_aftalebog(self, clinic: str):
|
|
167
|
+
"""Set clinic in aftalebog oversigt"""
|
|
168
|
+
|
|
169
|
+
## UNFINISHED
|
|
170
|
+
# Press clinic button
|
|
171
|
+
clinic_button = self.wait_for_control(
|
|
172
|
+
control_type=auto.PaneControl,
|
|
173
|
+
search_params={"AutomationId": "ButtonClinic"},
|
|
174
|
+
search_depth=8,
|
|
175
|
+
)
|
|
176
|
+
clinic_button.SetFocus()
|
|
177
|
+
clinic_button.SendKeys("{Enter}")
|
|
178
|
+
|
|
179
|
+
# Wait for popup window
|
|
180
|
+
find_clinic = self.wait_for_control(
|
|
181
|
+
control_type=auto.WindowControl,
|
|
182
|
+
search_params={"AutomationId": "FormFindClinics"},
|
|
183
|
+
search_depth=2,
|
|
184
|
+
)
|
|
185
|
+
# Get list and select clinic
|
|
186
|
+
clinic_list = self.find_element_by_property(
|
|
187
|
+
control=find_clinic, automation_id="ListClinics"
|
|
188
|
+
)
|
|
189
|
+
clinic_ctrls = [
|
|
190
|
+
_child
|
|
191
|
+
for _child in clinic_list.GetChildren()
|
|
192
|
+
if _child.ControlType == 50007
|
|
193
|
+
]
|
|
194
|
+
clinic_names = [
|
|
195
|
+
_child.Name
|
|
196
|
+
for _child in clinic_list.GetChildren()
|
|
197
|
+
if _child.ControlType == 50007
|
|
198
|
+
]
|
|
199
|
+
try:
|
|
200
|
+
slct_idx = clinic_names.index(clinic)
|
|
201
|
+
except Exception as e:
|
|
202
|
+
print(e)
|
|
203
|
+
print(f"Chosen clinic: {clinic}")
|
|
204
|
+
print("Possibilities: ")
|
|
205
|
+
print(" \n".join(clinic_names[::-1]))
|
|
206
|
+
# Search for the clinic if it is in the list (to get in focus)
|
|
207
|
+
find_clinic.SendKeys(clinic)
|
|
208
|
+
clinic_ctrls[slct_idx].SetFocus()
|
|
209
|
+
clinic_ctrls[slct_idx].SendKeys("{Enter}")
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
"""This module provides the main application handler for Solteq Tand, integrating various components."""
|
|
2
|
+
|
|
2
3
|
import os
|
|
4
|
+
|
|
3
5
|
import uiautomation as auto
|
|
4
6
|
|
|
7
|
+
from .aftalebog import AftalebogHandler
|
|
8
|
+
from .appointment import AppointmentHandler
|
|
5
9
|
from .base_ui import BaseUI
|
|
6
|
-
from .
|
|
10
|
+
from .clinic import ClinicHandler
|
|
7
11
|
from .document import DocumentHandler
|
|
8
|
-
from .appointment import AppointmentHandler
|
|
9
12
|
from .edi_portal import EDIHandler
|
|
10
|
-
from .clinic import ClinicHandler
|
|
11
13
|
from .event import EventHandler
|
|
12
14
|
from .journal_note import JournalNoteHandler
|
|
15
|
+
from .patient import PatientHandler
|
|
13
16
|
|
|
14
17
|
|
|
15
18
|
class SolteqTandApp(
|
|
@@ -20,7 +23,8 @@ class SolteqTandApp(
|
|
|
20
23
|
EDIHandler,
|
|
21
24
|
ClinicHandler,
|
|
22
25
|
EventHandler,
|
|
23
|
-
JournalNoteHandler
|
|
26
|
+
JournalNoteHandler,
|
|
27
|
+
AftalebogHandler,
|
|
24
28
|
):
|
|
25
29
|
"""
|
|
26
30
|
Main application handler for Solteq Tand, integrating various components.
|
|
@@ -62,9 +66,9 @@ class SolteqTandApp(
|
|
|
62
66
|
"""
|
|
63
67
|
self.app_window = self.wait_for_control(
|
|
64
68
|
auto.WindowControl,
|
|
65
|
-
{
|
|
69
|
+
{"AutomationId": "FormLogin"},
|
|
66
70
|
search_depth=3,
|
|
67
|
-
timeout=60
|
|
71
|
+
timeout=60,
|
|
68
72
|
)
|
|
69
73
|
self.app_window.SetFocus()
|
|
70
74
|
|
|
@@ -76,13 +80,13 @@ class SolteqTandApp(
|
|
|
76
80
|
|
|
77
81
|
login_button = self.app_window.PaneControl(AutomationId="ButtonLogin")
|
|
78
82
|
login_button.SetFocus()
|
|
79
|
-
login_button.SendKeys(
|
|
83
|
+
login_button.SendKeys("{ENTER}")
|
|
80
84
|
|
|
81
85
|
self.app_window = self.wait_for_control(
|
|
82
86
|
auto.WindowControl,
|
|
83
|
-
{
|
|
87
|
+
{"AutomationId": "FormFront"},
|
|
84
88
|
search_depth=2,
|
|
85
|
-
timeout=60
|
|
89
|
+
timeout=60,
|
|
86
90
|
)
|
|
87
91
|
|
|
88
92
|
def open_sub_tab(self, sub_tab_name: str):
|
|
@@ -97,7 +101,7 @@ class SolteqTandApp(
|
|
|
97
101
|
|
|
98
102
|
if not is_sub_tab_selected:
|
|
99
103
|
sub_tab_button.SetFocus()
|
|
100
|
-
sub_tab_button.SendKeys(
|
|
104
|
+
sub_tab_button.SendKeys("{ENTER}")
|
|
101
105
|
|
|
102
106
|
def open_tab(self, tab_name: str):
|
|
103
107
|
"""
|
|
@@ -122,13 +126,13 @@ class SolteqTandApp(
|
|
|
122
126
|
tab_button = self.find_element_by_property(
|
|
123
127
|
control=self.app_window,
|
|
124
128
|
control_type=auto.ControlType.TabItemControl,
|
|
125
|
-
name=tab_name_modified
|
|
129
|
+
name=tab_name_modified,
|
|
126
130
|
)
|
|
127
131
|
is_tab_selected = tab_button.GetPattern(10010).IsSelected
|
|
128
132
|
|
|
129
133
|
if not is_tab_selected:
|
|
130
134
|
tab_button.SetFocus()
|
|
131
|
-
tab_button.SendKeys(
|
|
135
|
+
tab_button.SendKeys("{ENTER}")
|
|
132
136
|
|
|
133
137
|
def open_from_main_menu(self, menu_item: str) -> None:
|
|
134
138
|
"""
|
|
@@ -137,20 +141,16 @@ class SolteqTandApp(
|
|
|
137
141
|
# Find hyperlink
|
|
138
142
|
menu_link = self.wait_for_control(
|
|
139
143
|
control_type=auto.HyperlinkControl,
|
|
140
|
-
search_params={
|
|
141
|
-
|
|
142
|
-
},
|
|
143
|
-
search_depth=5
|
|
144
|
+
search_params={"Name": menu_item},
|
|
145
|
+
search_depth=5,
|
|
144
146
|
)
|
|
145
147
|
|
|
146
148
|
menu_link.GetInvokePattern().Invoke()
|
|
147
149
|
|
|
148
150
|
self.app_window = self.wait_for_control(
|
|
149
151
|
control_type=auto.WindowControl,
|
|
150
|
-
search_params={
|
|
151
|
-
|
|
152
|
-
},
|
|
153
|
-
search_depth=2
|
|
152
|
+
search_params={"AutomationId": "FormBooking"},
|
|
153
|
+
search_depth=2,
|
|
154
154
|
)
|
|
155
155
|
|
|
156
156
|
def close_solteq_tand(self):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mbu_dev_shared_components
|
|
3
|
-
Version: 4.0
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: Shared components to use in RPA projects
|
|
5
5
|
Author-email: MBU <rpa@mbu.aarhus.dk>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -12,6 +12,7 @@ License-File: LICENSE
|
|
|
12
12
|
Requires-Dist: cryptography>=43.0.0
|
|
13
13
|
Requires-Dist: pyodbc>=5.1.0
|
|
14
14
|
Requires-Dist: python-dateutil==2.9.*
|
|
15
|
+
Requires-Dist: dotenv
|
|
15
16
|
Provides-Extra: getorganized
|
|
16
17
|
Requires-Dist: requests_ntlm>=1.2.0; extra == "getorganized"
|
|
17
18
|
Provides-Extra: google
|
|
@@ -31,6 +32,8 @@ Provides-Extra: sap
|
|
|
31
32
|
Provides-Extra: solteqtand
|
|
32
33
|
Requires-Dist: uiautomation; extra == "solteqtand"
|
|
33
34
|
Requires-Dist: pyodbc>=5.1.0; extra == "solteqtand"
|
|
35
|
+
Requires-Dist: psutil; extra == "solteqtand"
|
|
36
|
+
Requires-Dist: docx2pdf; extra == "solteqtand"
|
|
34
37
|
Provides-Extra: utils
|
|
35
38
|
Requires-Dist: python-dateutil==2.9.*; extra == "utils"
|
|
36
39
|
Requires-Dist: cryptography>=43.0.0; extra == "utils"
|
|
@@ -37,6 +37,7 @@ mbu_dev_shared_components/sap/__init__.py
|
|
|
37
37
|
mbu_dev_shared_components/sap/create_invoice.py
|
|
38
38
|
mbu_dev_shared_components/solteqtand/__init__.py
|
|
39
39
|
mbu_dev_shared_components/solteqtand/application/__init__.py
|
|
40
|
+
mbu_dev_shared_components/solteqtand/application/aftalebog.py
|
|
40
41
|
mbu_dev_shared_components/solteqtand/application/app_handler.py
|
|
41
42
|
mbu_dev_shared_components/solteqtand/application/appointment.py
|
|
42
43
|
mbu_dev_shared_components/solteqtand/application/base_ui.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mbu_dev_shared_components"
|
|
7
|
-
version = "4.0
|
|
7
|
+
version = "4.2.0"
|
|
8
8
|
authors = [
|
|
9
9
|
{ name="MBU", email="rpa@mbu.aarhus.dk" },
|
|
10
10
|
]
|
|
@@ -20,6 +20,7 @@ dependencies = [
|
|
|
20
20
|
"cryptography >= 43.0.0",
|
|
21
21
|
"pyodbc >= 5.1.0",
|
|
22
22
|
"python-dateutil == 2.9.*",
|
|
23
|
+
"dotenv",
|
|
23
24
|
]
|
|
24
25
|
|
|
25
26
|
[project.optional-dependencies]
|
|
@@ -49,6 +50,8 @@ sap = [
|
|
|
49
50
|
solteqtand = [
|
|
50
51
|
"uiautomation",
|
|
51
52
|
"pyodbc >= 5.1.0",
|
|
53
|
+
"psutil",
|
|
54
|
+
"docx2pdf",
|
|
52
55
|
]
|
|
53
56
|
utils = [
|
|
54
57
|
"python-dateutil == 2.9.*",
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|