Skip to content

Commit 252772d

Browse files
authored
Merge pull request #11 from itk-dev-rpa/develop
Develop
2 parents f76dce9 + 01e7b01 commit 252772d

File tree

14 files changed

+529
-195
lines changed

14 files changed

+529
-195
lines changed

.pylintrc

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
[pylint.messages_control]
22
disable =
3-
C0303, # Trailing whitespace
4-
C0103, # Variable names
5-
C0305, # Trailing newlines
6-
C0304, # Missing final line
73
C0301, # Line too long
84
I1101, E1101, # C-modules members
9-
W0621, # Redefine outer name
10-
R0913 # Too many arguments
5+
R0913, # Too many arguments
6+
R0914 # Too many local variables
117

12-
[MASTER]
13-
ignore-paths = ^tests/ # Ignore the tests folder

ITK_dev_shared_components/SAP/gridview_util.py

Lines changed: 20 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""This module provides static functions to peform common tasks with SAP GuiGridView COM objects."""
1+
"""This module provides static functions to perform common tasks with SAP GuiGridView COM objects."""
22

33
def scroll_entire_table(grid_view, return_to_top=False) -> None:
44
"""This function scrolls through the entire table to load all cells.
@@ -9,11 +9,13 @@ def scroll_entire_table(grid_view, return_to_top=False) -> None:
99
1010
Returns:
1111
_type_: _description_
12-
"""
12+
"""
13+
if grid_view.RowCount == 0 or grid_view.VisibleRowCount == 0:
14+
return
1315

1416
for i in range(0, grid_view.RowCount, grid_view.VisibleRowCount):
1517
grid_view.FirstVisibleRow = i
16-
18+
1719
if return_to_top:
1820
grid_view.FirstVisibleRow = 0
1921

@@ -28,7 +30,7 @@ def get_all_rows(grid_view, pre_load=True) -> tuple[tuple[str]]:
2830
2931
Returns:
3032
tuple[tuple[str]]: A 2D tuple of all cell values in the gridview.
31-
"""
33+
"""
3234

3335
if pre_load:
3436
scroll_entire_table(grid_view, True)
@@ -43,9 +45,9 @@ def get_all_rows(grid_view, pre_load=True) -> tuple[tuple[str]]:
4345
for c in columns:
4446
v = grid_view.GetCellValue(r, c)
4547
row_data.append(v)
46-
48+
4749
output.append(tuple(row_data))
48-
50+
4951
return tuple(output)
5052

5153

@@ -60,7 +62,7 @@ def get_row(grid_view, row:int, scroll_to_row=False) -> tuple[str]:
6062
6163
Returns:
6264
tuple[str]: A tuple of the row's data.
63-
"""
65+
"""
6466

6567
if scroll_to_row:
6668
grid_view.FirstVisibleRow = row
@@ -83,7 +85,7 @@ def iterate_rows(grid_view) -> tuple[str]:
8385
8486
Yields:
8587
tuple[str]: A tuple of the next row's data.
86-
"""
88+
"""
8789

8890
row = 0
8991
while row < grid_view.RowCount:
@@ -104,7 +106,7 @@ def get_column_titles(grid_view) -> tuple[str]:
104106
105107
Returns:
106108
tuple[str]: A tuple of the gridview's column titles.
107-
"""
109+
"""
108110

109111
return tuple(grid_view.GetColumnTitles(c)[0] for c in grid_view.ColumnOrder)
110112

@@ -123,7 +125,7 @@ def find_row_index_by_value(grid_view, column:str, value:str) -> int:
123125
124126
Returns:
125127
int: The index of the first row which column value matches the given value.
126-
"""
128+
"""
127129

128130
if column not in grid_view.ColumnOrder:
129131
raise ValueError(f"Column '{column}' not in grid_view")
@@ -132,14 +134,14 @@ def find_row_index_by_value(grid_view, column:str, value:str) -> int:
132134
# Only scroll when row isn't visible
133135
if not grid_view.FirstVisibleRow <= row <= grid_view.FirstVisibleRow + grid_view.VisibleRowCount-1:
134136
grid_view.FirstVisibleRow = row
135-
137+
136138
if grid_view.GetCellValue(row, column) == value:
137139
return row
138-
140+
139141
return -1
140142

141-
def find_all_row_indecies_by_value(grid_view, column:str, value:str) -> list[int]:
142-
"""Find all indecies of the rows where the given column's value
143+
def find_all_row_indices_by_value(grid_view, column:str, value:str) -> list[int]:
144+
"""Find all indices of the rows where the given column's value
143145
match the given value. Returns an empty list if no row is found.
144146
145147
Args:
@@ -151,8 +153,8 @@ def find_all_row_indecies_by_value(grid_view, column:str, value:str) -> list[int
151153
ValueError: If the column name doesn't exist in the grid view.
152154
153155
Returns:
154-
list[int]: A list of row indecies where the value matches.
155-
"""
156+
list[int]: A list of row indices where the value matches.
157+
"""
156158
if column not in grid_view.ColumnOrder:
157159
raise ValueError(f"Column '{column}' not in grid_view")
158160

@@ -162,39 +164,8 @@ def find_all_row_indecies_by_value(grid_view, column:str, value:str) -> list[int
162164
# Only scroll when row isn't visible
163165
if not grid_view.FirstVisibleRow <= row <= grid_view.FirstVisibleRow + grid_view.VisibleRowCount-1:
164166
grid_view.FirstVisibleRow = row
165-
167+
166168
if grid_view.GetCellValue(row, column) == value:
167169
rows.append(row)
168-
169-
return rows
170-
171-
172-
173-
174-
if __name__=='__main__':
175-
import win32com.client
176-
177-
SAP = win32com.client.GetObject("SAPGUI")
178-
app = SAP.GetScriptingEngine
179-
connection = app.Connections(0)
180-
session = connection.Sessions(0)
181-
182-
table = session.findById("wnd[0]/usr/cntlGRID1/shellcont/shell")
183-
184-
rows = find_all_row_indecies_by_value(table, "ZZ_PARTNER", '15879880')
185-
print(rows)
186-
table.setCurrentCell(rows[0], "ZZ_PARTNER")
187-
188-
# print(get_row(table, 1, True))
189-
190-
# scroll_entire_table(table)
191-
192-
# data = get_all_rows(table)
193-
# print(len(data), len(data[0]))
194-
195-
# for r in iterate_rows(table):
196-
# print(r)
197-
198-
# print(get_column_titles(table))
199-
200170

171+
return rows

ITK_dev_shared_components/SAP/multi_session.py

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,36 @@
1-
"""This module provides static function to handle multiple sessions of SAP.
1+
"""This module provides static functions to handle multiple sessions of SAP.
22
Using this module you can spawn multiple sessions and automatically execute
33
a function in parallel on the sessions."""
44

55
import time
66
import threading
7+
import math
78
from typing import Callable
9+
810
import pythoncom
911
import win32com.client
1012
import win32gui
1113

1214
def run_with_session(session_index:int, func:Callable, args:tuple) -> None:
13-
"""Run a function in a sepcific session based on the sessions index.
14-
This function is meant to be run inside a seperate thread.
15+
"""Run a function in a specific session based on the sessions index.
16+
This function is meant to be run inside a separate thread.
1517
The function must take a session object as its first argument.
1618
Note that this function will not spawn the sessions before running,
1719
use spawn_sessions to do that.
1820
"""
1921

2022
pythoncom.CoInitialize()
2123

22-
SAP = win32com.client.GetObject("SAPGUI")
23-
app = SAP.GetScriptingEngine
24+
sap = win32com.client.GetObject("SAPGUI")
25+
app = sap.GetScriptingEngine
2426
connection = app.Connections(0)
2527
session = connection.Sessions(session_index)
2628

2729
func(session, *args)
2830

2931
pythoncom.CoUninitialize()
3032

33+
3134
def run_batch(func:Callable, args:tuple[tuple], num_sessions=6) -> None:
3235
"""Run a function in parallel sessions.
3336
The function will be run {num_sessions} times with args[i] as input.
@@ -40,7 +43,7 @@ def run_batch(func:Callable, args:tuple[tuple], num_sessions=6) -> None:
4043
for i in range(num_sessions):
4144
t = ExThread(target=run_with_session, args=(i, func, args[i]))
4245
threads.append(t)
43-
46+
4447
for t in threads:
4548
t.start()
4649
for t in threads:
@@ -49,6 +52,7 @@ def run_batch(func:Callable, args:tuple[tuple], num_sessions=6) -> None:
4952
if t.error:
5053
raise t.error
5154

55+
5256
def run_batches(func:Callable, args:tuple[tuple], num_sessions=6):
5357
"""Run a function in parallel batches.
5458
This function runs the input function for each set of arguments in args.
@@ -62,16 +66,26 @@ def run_batches(func:Callable, args:tuple[tuple], num_sessions=6):
6266
batch = args[b:b+num_sessions]
6367
run_batch(func, args, len(batch))
6468

65-
def spawn_sessions(num_sessions=6) -> list:
69+
70+
def spawn_sessions(num_sessions=6) -> tuple:
6671
"""A function to spawn multiple sessions of SAP.
6772
This function will attempt to spawn the desired number of sessions.
68-
If the current number of open sessions exceeds the desired number of sessions
73+
If the current number of already open sessions exceeds the desired number of sessions
6974
the already open sessions will not be closed to match the desired number.
7075
The number of sessions must be between 1 and 6.
71-
Returns a list of all open sessions.
76+
77+
Args:
78+
num_sessions: The number of sessions desired. Defaults to 6.
79+
80+
Raises:
81+
ValueError: If the number of sessions is not between 1 and 6.
82+
83+
Returns:
84+
tuple: A tuple of all currently open sessions.
7285
"""
73-
SAP = win32com.client.GetObject("SAPGUI")
74-
app = SAP.GetScriptingEngine
86+
87+
sap = win32com.client.GetObject("SAPGUI")
88+
app = sap.GetScriptingEngine
7589
connection = app.Connections(0)
7690
session = connection.Sessions(0)
7791

@@ -82,25 +96,17 @@ def spawn_sessions(num_sessions=6) -> list:
8296

8397
for _ in range(num_sessions - connection.Sessions.count):
8498
session.CreateSession()
85-
99+
86100
# Wait for the sessions to spawn
87101
while connection.Sessions.count < num_sessions:
88102
time.sleep(0.1)
89103

90-
sessions = list(connection.Sessions)
104+
sessions = tuple(connection.Sessions)
91105
num_sessions = len(sessions)
92106

93-
if num_sessions == 1:
94-
c = 1
95-
elif num_sessions <= 4:
96-
c = 2
97-
elif num_sessions <= 6:
98-
c = 3
99-
100-
if num_sessions < 3:
101-
r = 1
102-
else:
103-
r = 2
107+
# Calculate number of columns and rows
108+
c = math.ceil(math.sqrt(num_sessions))
109+
r = math.ceil(num_sessions / c)
104110

105111
w, h = 1920//c, 1040//r
106112

@@ -111,20 +117,22 @@ def spawn_sessions(num_sessions=6) -> list:
111117
x = i % c * w
112118
y = i // c * h
113119
win32gui.MoveWindow(hwnd, x, y, w, h, True)
114-
120+
115121
return sessions
116122

117-
def get_all_SAP_sessions() -> tuple:
123+
124+
def get_all_SAP_sessions() -> tuple: # pylint: disable=invalid-name
118125
"""Returns a tuple of all open SAP sessions (on connection index 0).
119126
120127
Returns:
121128
tuple: A tuple of SAP GuiSession objects.
122129
"""
123-
SAP = win32com.client.GetObject("SAPGUI")
124-
app = SAP.GetScriptingEngine
130+
sap = win32com.client.GetObject("SAPGUI")
131+
app = sap.GetScriptingEngine
125132
connection = app.Connections(0)
126133
return tuple(connection.Sessions)
127134

135+
128136
class ExThread(threading.Thread):
129137
"""A thread with a handle to get an exception raised inside the thread: ExThread.error"""
130138
def __init__(self, *args, **kwargs):
@@ -136,5 +144,3 @@ def run(self):
136144
self._target(*self._args, **self._kwargs)
137145
except Exception as e: # pylint: disable=broad-exception-caught
138146
self.error = e
139-
140-

ITK_dev_shared_components/SAP/opret_kundekontakt.py

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
"""This module provides a single function to conviniently peform the action 'opret-kundekontaker' in SAP."""
1+
"""This module provides a single function to conveniently peform the action 'opret-kundekontaker' in SAP."""
22

33
from typing import Literal
44
import win32clipboard
55
from ITK_dev_shared_components.SAP import tree_util
66

77

8-
def opret_kundekontakter(session, fp:str, aftaler:list[str] | None,
9-
art:Literal[' ', 'Automatisk', 'Fakturagrundlag', 'Fuldmagt ifm. værge', 'Konverteret', 'Myndighedshenvend.', 'Orientering', 'Returpost', 'Ringeaktivitet', 'Skriftlig henvend.', 'Telefonisk henvend.'],
8+
def opret_kundekontakter(session, fp:str, aftaler:list[str] | None,
9+
art:Literal[' ', 'Automatisk', 'Fakturagrundlag', 'Fuldmagt ifm. værge', 'Konverteret', 'Myndighedshenvend.', 'Orientering', 'Returpost', 'Ringeaktivitet', 'Skriftlig henvend.', 'Telefonisk henvend.'],
1010
notat:str, lock=None) -> None:
1111
"""Creates a kundekontakt on the given FP and aftaler.
1212
@@ -41,33 +41,25 @@ def opret_kundekontakter(session, fp:str, aftaler:list[str] | None,
4141

4242
# Go to editor and paste (lock if multithreaded)
4343
session.findById("wnd[0]/usr/subNOTICE:SAPLEENO:1002/btnEENO_TEXTE-EDITOR").press()
44-
if lock:
44+
if lock:
4545
lock.acquire()
46-
_setClipboard(notat)
46+
_set_clipboard(notat)
4747
session.findById("wnd[0]/tbar[1]/btn[9]").press()
48-
if lock:
48+
if lock:
4949
lock.release()
5050

5151
# Back and save
5252
session.findById("wnd[0]/tbar[0]/btn[3]").press()
5353
session.findById("wnd[0]/tbar[0]/btn[11]").press()
5454

5555

56-
def _setClipboard(text:str) -> None:
56+
def _set_clipboard(text:str) -> None:
57+
"""Private function to set text to the clipboard.
58+
59+
Args:
60+
text: Text to set to clipboard.
61+
"""
5762
win32clipboard.OpenClipboard()
5863
win32clipboard.EmptyClipboard()
5964
win32clipboard.SetClipboardText(text)
6065
win32clipboard.CloseClipboard()
61-
62-
63-
if __name__ == '__main__':
64-
from ITK_dev_shared_components.SAP import multi_session
65-
from datetime import datetime
66-
67-
session = multi_session.spawn_sessions(1)[0]
68-
fp = '25564617'
69-
aftaler = ['2291987', '2421562', '2311094']
70-
art = 'Orientering'
71-
notat = 'Test '+ str(datetime.now())
72-
73-
opret_kundekontakter(session, fp, aftaler, 'Automatisk', notat)

0 commit comments

Comments
 (0)